diff --git a/.docker/Dockerfile b/.docker/Dockerfile index d7011ee0..550d2a57 100644 --- a/.docker/Dockerfile +++ b/.docker/Dockerfile @@ -7,12 +7,12 @@ ENV CI=true COPY docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/*.csproj CodeBeam.Website/ COPY docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/*.csproj CodeBeam.Website.Client/ -RUN dotnet restore CodeBeam.Website/CodeBeam.UltimateAuth.Docs.Wasm.csproj +RUN dotnet restore CodeBeam.Website/CodeBeam.UltimateAuth.Docs.Wasm.csproj -p:UltimateAuthBuildTargetFrameworks=net10.0 COPY . . RUN dotnet publish docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.csproj \ - -c Release -o /app/publish + -c Release -p:UltimateAuthBuildTargetFrameworks=net10.0 -o /app/publish # RUNTIME FROM mcr.microsoft.com/dotnet/aspnet:10.0 diff --git a/.docker/Dockerfile.identitylab b/.docker/Dockerfile.identitylab index 24974c06..b786705d 100644 --- a/.docker/Dockerfile.identitylab +++ b/.docker/Dockerfile.identitylab @@ -5,11 +5,13 @@ WORKDIR /src COPY . . RUN dotnet restore \ - apps/identity-lab/CodeBeam.UltimateAuth.IdentityLab/CodeBeam.UltimateAuth.IdentityLab.csproj + apps/identity-lab/CodeBeam.UltimateAuth.IdentityLab/CodeBeam.UltimateAuth.IdentityLab.csproj \ + -p:UltimateAuthBuildTargetFrameworks=net10.0 RUN dotnet publish \ apps/identity-lab/CodeBeam.UltimateAuth.IdentityLab/CodeBeam.UltimateAuth.IdentityLab.csproj \ -c Release \ + -p:UltimateAuthBuildTargetFrameworks=net10.0 \ --no-restore \ -o /app/publish diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 69e6789d..cebd62a9 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,13 +1,15 @@ -name: Deploy UltimateAuth +name: Docs Site on: + pull_request: push: branches: - master workflow_dispatch: concurrency: - group: ultimateauth-deploy + # PR updates may cancel their own validation, never a production deployment. + group: ${{ github.event_name == 'pull_request' && format('docs-site-pr-{0}', github.event.pull_request.number) || 'ultimateauth-deploy' }} cancel-in-progress: true permissions: @@ -15,35 +17,74 @@ permissions: packages: write jobs: - build-and-deploy: + changes: + name: Changes + if: github.event_name == 'pull_request' runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + outputs: + site: ${{ steps.filter.outputs.site }} + steps: + - name: Detect changed files + id: filter + uses: dorny/paths-filter@v3 + with: + filters: | + site: + - 'docs/**' + - 'src/utilities/CodeBeam.UltimateAuth.DocsBuilder/**' + - 'Directory.Build.props' + - 'Directory.Build.targets' + - 'Directory.Packages.props' + - 'global.json' + - 'NuGet.Config' + - 'nuget.config' + - '.dockerignore' + - '.docker/Dockerfile' + - '.docker/docker-compose.yml' + - '.github/workflows/deploy.yml' + build: + name: Docker Build + needs: changes + # Master and manual builds do not depend on a PR-only detector succeeding. + if: >- + !cancelled() && + (github.event_name != 'pull_request' || + (needs.changes.result == 'success' && needs.changes.outputs.site == 'true')) + runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - - name: Login to GitHub Container Registry + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' uses: docker/login-action@v3 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Setup Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Build and push image uses: docker/build-push-action@v6 with: context: . file: .docker/Dockerfile - push: true + push: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/master' }} tags: | ghcr.io/codebeamorg/ultimateauth:master ghcr.io/codebeamorg/ultimateauth:${{ github.sha }} cache-from: type=gha cache-to: type=gha,mode=max + deploy: + name: Deploy + needs: build + if: github.event_name != 'pull_request' && github.ref == 'refs/heads/master' + runs-on: ubuntu-latest + steps: - name: Deploy to Server uses: appleboy/ssh-action@v1.0.3 with: diff --git a/.github/workflows/identitylab-image.yml b/.github/workflows/identitylab-image.yml index d0df3b0f..fe17a6f3 100644 --- a/.github/workflows/identitylab-image.yml +++ b/.github/workflows/identitylab-image.yml @@ -1,26 +1,10 @@ -name: Identity Lab Docker Image +name: Identity Lab on: pull_request: - paths: - - "apps/identity-lab/**" - - "src/**" - - "samples/**" - - ".docker/Dockerfile.identitylab" - - ".docker/docker-compose.identitylab.yml" - - ".github/workflows/identitylab-image.yml" - push: branches: - lab - paths: - - "apps/identity-lab/**" - - "src/**" - - "samples/**" - - ".docker/Dockerfile.identitylab" - - ".docker/docker-compose.identitylab.yml" - - ".github/workflows/identitylab-image.yml" - workflow_dispatch: permissions: @@ -28,20 +12,59 @@ permissions: packages: write concurrency: - group: identitylab-deploy + # PR validation cannot block or replace a production deployment run. + group: ${{ github.event_name == 'pull_request' && format('identitylab-pr-{0}', github.event.pull_request.number) || 'identitylab-deploy' }} cancel-in-progress: false jobs: - build: + changes: + name: Changes + if: github.event_name != 'workflow_dispatch' runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + outputs: + image: ${{ steps.filter.outputs.image }} + steps: + - name: Checkout for push comparison + if: github.event_name != 'pull_request' + uses: actions/checkout@v4 + - name: Detect changed files + id: filter + uses: dorny/paths-filter@v3 + with: + base: ${{ github.ref }} + filters: | + image: + - 'apps/identity-lab/**' + - 'src/**' + - 'samples/**' + - 'Directory.Build.props' + - 'Directory.Build.targets' + - 'Directory.Packages.props' + - 'global.json' + - 'NuGet.Config' + - 'nuget.config' + - '.dockerignore' + - '.docker/Dockerfile.identitylab' + - '.docker/docker-compose.identitylab.yml' + - '.github/workflows/identitylab-image.yml' + build: + name: Docker Build + needs: changes + # Manual image builds must also run when the PR/push detector is skipped. + if: >- + !cancelled() && + (github.event_name == 'workflow_dispatch' || + (needs.changes.result == 'success' && needs.changes.outputs.image == 'true')) + runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@v4 - - name: Set up Docker Buildx uses: docker/setup-buildx-action@v3 - - name: Log in to GHCR if: github.event_name != 'pull_request' uses: docker/login-action@v3 @@ -49,7 +72,6 @@ jobs: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - - name: Build and push Identity Lab uses: docker/build-push-action@v6 with: @@ -61,7 +83,7 @@ jobs: cache-to: type=gha,mode=max deploy: - name: Deploy Identity Lab + name: Deploy needs: build if: github.event_name != 'pull_request' && github.ref == 'refs/heads/lab' runs-on: ubuntu-latest diff --git a/.github/workflows/javascript-tests.yml b/.github/workflows/javascript-tests.yml index afe34b2b..d791c003 100644 --- a/.github/workflows/javascript-tests.yml +++ b/.github/workflows/javascript-tests.yml @@ -1,4 +1,4 @@ -name: JavaScript Client Tests +name: JS Client on: pull_request: @@ -11,40 +11,40 @@ permissions: contents: read jobs: - detect-changes: - name: Detect JavaScript Changes + changes: + name: Changes runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read outputs: javascript: ${{ steps.filter.outputs.javascript }} - steps: - - name: Checkout + - name: Checkout for push comparison + if: github.event_name != 'pull_request' uses: actions/checkout@v4 - - name: Detect changed files id: filter uses: dorny/paths-filter@v3 with: + base: ${{ github.ref }} filters: | javascript: - 'src/client/CodeBeam.UltimateAuth.Client.Blazor/TScripts/**' - 'tests/CodeBeam.UltimateAuth.Tests.Unit/javascript/**' - '.github/workflows/javascript-tests.yml' - javascript-tests: - name: Node.js Client Tests - needs: detect-changes - if: needs.detect-changes.outputs.javascript == 'true' + tests: + name: Tests + needs: changes + if: needs.changes.outputs.javascript == 'true' runs-on: ubuntu-latest - steps: - name: Checkout uses: actions/checkout@v4 - - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: '22' - - name: Run JavaScript tests run: node --test tests/CodeBeam.UltimateAuth.Tests.Unit/javascript/*.test.cjs diff --git a/.github/workflows/nuget-publish.yml b/.github/workflows/nuget-publish.yml index f7822605..5bbdf1d2 100644 --- a/.github/workflows/nuget-publish.yml +++ b/.github/workflows/nuget-publish.yml @@ -24,13 +24,26 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Resolve supported SDKs + id: frameworks + env: + DOTNET_NOLOGO: '1' + shell: bash + run: | + frameworks=$(dotnet msbuild src/CodeBeam.UltimateAuth.Core/CodeBeam.UltimateAuth.Core.csproj -nologo -getProperty:UltimateAuthTargetFrameworks) + { + echo 'sdks<> "$GITHUB_OUTPUT" + - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: | - 8.0.x - 9.0.x - 10.0.x + dotnet-version: ${{ steps.frameworks.outputs.sdks }} - name: Verify package version shell: bash diff --git a/.github/workflows/ultimateauth-ci.yml b/.github/workflows/ultimateauth-ci.yml index 0b38ddf7..92d96423 100644 --- a/.github/workflows/ultimateauth-ci.yml +++ b/.github/workflows/ultimateauth-ci.yml @@ -1,4 +1,4 @@ -name: UltimateAuth CI +name: UAuth CI on: push: @@ -8,54 +8,185 @@ on: tags: - 'v*' pull_request: - branches: - - dev - - 'release/**' workflow_dispatch: jobs: - build-test-coverage: - name: Build, Test & Coverage + frameworks: + name: Framework Validation runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.policy.outputs.matrix }} + latest: ${{ steps.policy.outputs.latest }} + latest-display: ${{ steps.policy.outputs.latest-display }} + sdks: ${{ steps.policy.outputs.sdks }} + solution-frameworks: ${{ steps.policy.outputs.solution-frameworks }} + steps: + - uses: actions/checkout@v4 + - name: Audit projects and generate compatibility matrix + id: policy + env: + DOTNET_NOLOGO: '1' + shell: bash + run: | + python3 - <<'PYTHON' + import concurrent.futures + import json + import os + from pathlib import Path + import re + import subprocess + import xml.etree.ElementTree as ET + + + def properties(project, names): + return json.loads(subprocess.check_output( + ["dotnet", "msbuild", str(project), "-nologo", "-getProperty:" + ",".join(names)], + text=True))["Properties"] + + config = properties("src/CodeBeam.UltimateAuth.Core/CodeBeam.UltimateAuth.Core.csproj", + ["UltimateAuthTargetFrameworks", "UltimateAuthLatestTargetFramework"]) + frameworks = config["UltimateAuthTargetFrameworks"].split(";") + assert frameworks and all(re.fullmatch(r"net\d+\.\d+", f) for f in frameworks), "Invalid supported frameworks" + version = lambda f: tuple(map(int, f[3:].split("."))) + assert frameworks == sorted(set(frameworks), key=version), "Frameworks must be unique and ascending" + latest = config["UltimateAuthLatestTargetFramework"] + assert latest == max(frameworks, key=version), "Incorrect latest test target" + projects = sorted(p for base in ("src", "samples", "apps", "docs", "tests") + for p in Path(base).rglob("*.csproj") if not {"obj", "bin"}.intersection(p.parts)) + with concurrent.futures.ThreadPoolExecutor(max_workers=4) as pool: + metadata = dict(zip(projects, pool.map(lambda p: properties( + p, ["TargetFramework", "TargetFrameworks", "IsPackable"]), projects))) + targets = lambda info: (info["TargetFrameworks"] or info["TargetFramework"]).split(";") + solution = {p.attrib["Path"].replace("\\", "/") for p in ET.parse("UltimateAuth.slnx").iter("Project")} + assert {p.as_posix() for p in projects} <= solution, "Projects are missing from the full solution build" + expected = {p for p in projects if p.parts[0] == "src" and "utilities" not in p.parts + and p.parent.name != "CodeBeam.UltimateAuth.Client.JsMinifier"} + expected.add(Path("samples/CodeBeam.UltimateAuth.Sample.Seed/CodeBeam.UltimateAuth.Sample.Seed.csproj")) + packages = {p: info for p, info in metadata.items() if info["IsPackable"].lower() == "true"} + assert expected <= packages.keys(), "An expected library is missing or no longer packable" + for p in expected: + assert targets(packages[p]) == frameworks, f"Missing supported library target: {p}" + tests = [p for p in projects if p.parts[0] == "tests"] + assert tests, "No test projects discovered" + for p in tests: + assert targets(metadata[p]) == [latest], f"Tests must target only {latest}: {p}" + required = {latest} | {info["TargetFramework"] for info in metadata.values() if info["TargetFramework"]} + assert required <= set(frameworks), "Retarget single-target exceptions before dropping their library framework" + matrix = {"include": []} + for framework in frameworks: + included = [p.as_posix() for p, info in packages.items() if framework in targets(info)] + assert included, f"No packages selected for {framework}" + matrix["include"].append({"framework": framework, "display": framework[3:].removesuffix(".0"), + "sdk": framework[3:] + ".x", + "projects": "\n".join(included)}) + for p, info in packages.items(): + assert set(targets(info)) <= set(frameworks), f"Uncovered package framework: {p}" + with open(os.environ["GITHUB_OUTPUT"], "a") as output: + output.write("matrix=" + json.dumps(matrix, separators=(",", ":")) + "\n") + output.write("latest=" + latest + "\n") + output.write("latest-display=" + latest[3:].removesuffix(".0") + "\n") + output.write("solution-frameworks=" + "%3B".join(sorted(required, key=version)) + "\n") + output.write("sdks<- - dotnet test tests/CodeBeam.UltimateAuth.IdentityLab.Tests + dotnet test --project tests/CodeBeam.UltimateAuth.IdentityLab.Tests/CodeBeam.UltimateAuth.IdentityLab.Tests.csproj --configuration Release - --collect:"XPlat Code Coverage" - --logger "trx;LogFileName=identity-lab.trx" + --framework ${{ needs.frameworks.outputs.latest }} + -p:UltimateAuthBuildTargetFrameworks=${{ needs.frameworks.outputs.solution-frameworks }} + --coverlet + --coverlet-output-format cobertura + --coverlet-output-format json + --coverlet-include "[CodeBeam.UltimateAuth.IdentityLab]*" + --coverlet-exclude-by-file "**/Migrations/**/*.cs" + --report-trx + --report-trx-filename identity-lab.trx --results-directory TestResults/IdentityLab - --settings tests/CodeBeam.UltimateAuth.IdentityLab.Tests/identity-lab.runsettings - name: Generate Identity Lab coverage summary if: always() @@ -87,7 +224,7 @@ jobs: import xml.etree.ElementTree as ET reports = glob.glob( - "TestResults/IdentityLab/**/coverage.cobertura.xml", + "TestResults/IdentityLab/**/coverage.cobertura*.xml", recursive=True ) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 67cbcb91..130e1ec1 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -99,3 +99,42 @@ Once the CLA process is implemented, contributors may be required to sign the ap Be respectful, assume good intentions, and help others learn. Different experience levels and perspectives make the project better. **Thank you for helping build UltimateAuth.** + +## Framework targeting and CI + +`UltimateAuthTargetFrameworks` in `Directory.Build.props` is the supported library +framework list. Keep it unique and in ascending version order. The last entry +becomes `UltimateAuthLatestTargetFramework`, which all five test projects inherit +from `tests/Directory.Build.props`. Tests, including rate-limiting compatibility +tests, run once on that latest framework; older frameworks receive package build +verification rather than repeated test runs. + +The 33 library projects under `src/` and the packable `Sample.Seed` project use +`UltimateAuthBuildTargetFrameworks`, which defaults to the supported list. CI can +select a supported build subset through that property without changing the +frameworks of single-target applications. `Sample.Shared.Hub` is a separate, +packable net10-only Razor library. Applications, other samples, docs, and the two +build tools retain their explicit net10 targets. Docker builds also restrict library +builds to their net10 application target, so adding a newer library framework +does not require upgrading those container SDKs. + +The CI policy job queries evaluated MSBuild properties for every project. It +checks solution membership, all expected library targets, and latest-only test +targets, then generates the compatibility matrix and an exact package/framework +table in the job summary. Each matrix job pins `global.json` **in its own checkout** +to the SDK installed for its framework, so a newer preinstalled SDK cannot hide +an older-SDK build failure. The committed `global.json` still selects MTP without +pinning an SDK. + +The test job retains the full solution build, but builds libraries only for the +latest framework and the frameworks needed by intentional single-target projects. +Identity Lab keeps its separate PostgreSQL job, TRX output, and assembly-scoped +coverage. The publishing workflow also derives its SDK list from the central +property. + +For example, changing the central list to `net10.0;net11.0` automatically generates +two SDK build jobs and moves all regular tests to net11. No workflow matrix edits +are needed. Framework-specific package references (notably Blazor, EF Core, and +TestHost) still need compatible versions when adding a new framework. Retarget +net10-only application/tool exceptions before eventually removing net10 itself; +CI rejects that omission rather than silently excluding projects. diff --git a/Directory.Build.props b/Directory.Build.props index 28108cd4..941fc4da 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,5 +1,11 @@ + + net8.0;net9.0;net10.0 + $([System.Text.RegularExpressions.Regex]::Match('$(UltimateAuthTargetFrameworks)', '[^;]+$').Value) + + $(UltimateAuthTargetFrameworks) + 0.2.0-preview.1 $(NoWarn);1591 diff --git a/README.md b/README.md index abaaeb25..a157e2c6 100644 --- a/README.md +++ b/README.md @@ -34,27 +34,40 @@ UltimateAuth is an open-source auth framework with platform-level capabilities t --- ## Roadmap -| Phase | Version | Scope | Status | Release Date | -| ----------------------- | ------------- | ----------------------------------------- | -------------- | ------------ | -| First Preview | 0.1.0-preview | "Stable" Preview Core | ✅ Completed | 07.04.2026 | +| Phase | Version | Scope | Status | Release Date | +| ----------------------- | ------------- | ----------------------------------------- | -------------- | ------------ | +| First Preview | 0.1.0-preview | Preview Core | ✅ Completed | 07.04.2026 | | First Release* | 0.1.0 | Documented & Quality Tested (Client Public API Locked) | ✅ Completed | 12.10.2026 | -| Product Expansion | 0.2.0 | Full Auth Modes, Rate Limiting | 🟡 In Progress | Q4 2026 | -| Security Expansion | 0.3.0 | MFA, Reauth | 🟡 In Progress | Q4 2026 | -| Infrastructure Expansion| 0.4.0 | Redis, Distributed Cache, Password Hasher | 🔜 Planned | Q1 2027 | -| Multi-Tenant Expansion | 0.5.0 | Multi tenant management | 🔜 Planned | Q1 2027 | -| Extensibility Expansion | 0.6.0 | Audit, events, hooks | 🔜 Planned | Q1 2027 | -| Performance Expansion | 0.7.0 | Benchmarks, caching | 🔜 Planned | Q1 2027 | -| Ecosystem Expansion | 0.8.0 | Migration tools | 🔜 Planned | Q2 2027 | -| v1.0 | 1.0.0 | Locked API, align with .NET 11 | 🔜 Planned | Q2 2027 | +| Product Expansion | 0.2.0 | Full Auth Modes, Rate Limiting | 🟡 In Progress | Q4 2026 | +| Security Expansion | 0.3.0 | MFA, Reauth | 🟡 In Progress | Q1 2027 | +| Infrastructure Expansion| 0.4.0 | Redis, Distributed Cache, Password Hashers | 🔜 Planned | Q1 2027 | +| Multi-Tenant Expansion | 0.5.0 | Multi tenant management | 🔜 Planned | Q2 2027 | +| Extensibility Expansion | 0.6.0 | Audit, events, hooks | 🔜 Planned | Q2 2027 | +| Performance Expansion | 0.7.0 | Benchmarks, caching | 🔜 Planned | Q3 2027 | +| Ecosystem Expansion | 0.8.0 | Migration tools | 🔜 Planned | Q3 2027 | +| v1.0 | 1.0.0 | Locked API, align with .NET 12 | 🔜 Planned | Q4 2027 | -*v 0.1.0 already provides a skeleton and extensibility points of multi tenancy, MFA, reauth etc. Expansion releases will enhance these areas. +> **UltimateAuth was architected from its first release with extensibility points and foundational support for advanced capabilities, including multi-tenancy, MFA, and reauthentication. Subsequent releases build upon this existing architecture, expanding implementations, improvements and production-ready functionality rather than introducing these concepts from scratch.* > The project roadmap is actively maintained as a GitHub issue: - -👉 https://github.com/CodeBeamOrg/UltimateAuth/issues/8 +> +> [![GitHub Roadmap](https://img.shields.io/badge/GitHub-View_Roadmap-181717?logo=github&logoColor=white&style=flat-square)](https://github.com/CodeBeamOrg/UltimateAuth/issues/8) We keep it up-to-date with current priorities, planned features, and progress. Feel free to follow, comment, or contribute ideas. +### Project Milestones + +UltimateAuth is designed to deliver a simple, consistent developer experience from its very first release. Each milestone expands its capabilities and supported scenarios, rather than introducing usability or architectural maturity from scratch. + +| Milestone | Version | Target | +|---|---|---| +| **Developer Foundation** | 0.1.x | A functional, security-conscious authentication framework with a complete core architecture, extensibility, and a developer-first API. Suitable for development, early adoption, and production use following application-specific evaluation. | +| **Production Expansion** | 0.2.0 | Ready to power real-world applications, particularly small and medium-sized products, with expanded authentication modes, built-in rate limiting, and a proven integration experience. Larger applications can also adopt UltimateAuth when its available capabilities meet their requirements. | +| **Advanced Security & Scale** | 0.4.0 | Extends production capabilities with MFA, reauthentication, distributed infrastructure, and additional security options for applications with more demanding requirements. Builds on the same simple developer experience. | +| **Migration & Adoption** | 0.8.0 | Makes adopting UltimateAuth in existing applications easier through migration tools, guidance, and established integration patterns, without requiring new applications to wait for this milestone. | + +**The UltimateAuth philosophy:** Simple from day one, more capable with every release. Version milestones describe expanding capabilities — not a progression from difficult to easy, or from unusable to usable. + --- ### 💜 Join the UltimateAuth Community @@ -347,7 +360,7 @@ Create test accounts, manage identities and sessions, explore roles and permissi **Powered by UltimateAuth, not simulated.** The demonstrated identity capabilities use the framework itself, so you can see what UltimateAuth can do before integrating it into your own application. -🧪 **Identity Lab — Interactive Sandbox** *(Link coming soon)* +🧪 **[Identity Lab](https://lab.ultimateauth.com)** --- diff --git a/apps/identity-lab/CodeBeam.UltimateAuth.IdentityLab/CodeBeam.UltimateAuth.IdentityLab.csproj b/apps/identity-lab/CodeBeam.UltimateAuth.IdentityLab/CodeBeam.UltimateAuth.IdentityLab.csproj index 5987c73f..de41bbeb 100644 --- a/apps/identity-lab/CodeBeam.UltimateAuth.IdentityLab/CodeBeam.UltimateAuth.IdentityLab.csproj +++ b/apps/identity-lab/CodeBeam.UltimateAuth.IdentityLab/CodeBeam.UltimateAuth.IdentityLab.csproj @@ -10,14 +10,14 @@ - - + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/apps/identity-lab/CodeBeam.UltimateAuth.IdentityLab/Components/Pages/Dashboard/MyAccessPage.razor b/apps/identity-lab/CodeBeam.UltimateAuth.IdentityLab/Components/Pages/Dashboard/MyAccessPage.razor index 2f27fb8b..5a23e614 100644 --- a/apps/identity-lab/CodeBeam.UltimateAuth.IdentityLab/Components/Pages/Dashboard/MyAccessPage.razor +++ b/apps/identity-lab/CodeBeam.UltimateAuth.IdentityLab/Components/Pages/Dashboard/MyAccessPage.razor @@ -211,7 +211,7 @@ @foreach (var group in Scenarios.GroupBy(x => x.Group)) { - + @foreach (var scenario in group) { diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj index 69a55d41..a4e8ea48 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/CodeBeam.UltimateAuth.Docs.Wasm.Client.csproj @@ -11,10 +11,10 @@ - + - - + + diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor index 852c35a8..8b993cce 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Layout/MainLayout.razor @@ -110,7 +110,7 @@ - © @DateTime.Now.Year CodeBeam. + © @DateTime.Now.Year CodeBeam. diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/Samples.razor b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/Samples.razor index e69e4bc9..668e5ba0 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/Samples.razor +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.Client/Pages/Samples.razor @@ -5,7 +5,7 @@ Samples Explore working examples and see UltimateAuth in action. - Identity Lab (hosted live demo) is coming soon. + Identity Lab (live sandbox) is also available. diff --git a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.csproj b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.csproj index be947b19..d84cbca3 100644 --- a/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.csproj +++ b/docs/website/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm/CodeBeam.UltimateAuth.Docs.Wasm.csproj @@ -10,7 +10,7 @@ - + diff --git a/global.json b/global.json new file mode 100644 index 00000000..3140116d --- /dev/null +++ b/global.json @@ -0,0 +1,5 @@ +{ + "test": { + "runner": "Microsoft.Testing.Platform" + } +} diff --git a/samples/CodeBeam.UltimateAuth.Sample.Seed/CodeBeam.UltimateAuth.Sample.Seed.csproj b/samples/CodeBeam.UltimateAuth.Sample.Seed/CodeBeam.UltimateAuth.Sample.Seed.csproj index cc80e9e8..d961b7f3 100644 --- a/samples/CodeBeam.UltimateAuth.Sample.Seed/CodeBeam.UltimateAuth.Sample.Seed.csproj +++ b/samples/CodeBeam.UltimateAuth.Sample.Seed/CodeBeam.UltimateAuth.Sample.Seed.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Sample.Seed diff --git a/samples/CodeBeam.UltimateAuth.Sample.Shared.Hub/CodeBeam.UltimateAuth.Sample.Shared.Hub.csproj b/samples/CodeBeam.UltimateAuth.Sample.Shared.Hub/CodeBeam.UltimateAuth.Sample.Shared.Hub.csproj index 5aa2b7e0..967a1f52 100644 --- a/samples/CodeBeam.UltimateAuth.Sample.Shared.Hub/CodeBeam.UltimateAuth.Sample.Shared.Hub.csproj +++ b/samples/CodeBeam.UltimateAuth.Sample.Shared.Hub/CodeBeam.UltimateAuth.Sample.Shared.Hub.csproj @@ -1,9 +1,10 @@ - + net10.0 enable enable + false @@ -12,7 +13,7 @@ - + diff --git a/samples/CodeBeam.UltimateAuth.Sample.Shared/CodeBeam.UltimateAuth.Sample.Shared.csproj b/samples/CodeBeam.UltimateAuth.Sample.Shared/CodeBeam.UltimateAuth.Sample.Shared.csproj index f1d71e85..b1b27775 100644 --- a/samples/CodeBeam.UltimateAuth.Sample.Shared/CodeBeam.UltimateAuth.Sample.Shared.csproj +++ b/samples/CodeBeam.UltimateAuth.Sample.Shared/CodeBeam.UltimateAuth.Sample.Shared.csproj @@ -1,4 +1,4 @@ - + net10.0 @@ -13,12 +13,12 @@ - - - - - - + + + + + + diff --git a/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore.csproj b/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore.csproj index afc22ae3..c82d7e9a 100644 --- a/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore.csproj +++ b/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore/CodeBeam.UltimateAuth.Sample.UAuthHub.EFCore.csproj @@ -9,17 +9,17 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.csproj b/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.csproj index 2012f030..6106524c 100644 --- a/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.csproj +++ b/samples/UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub/CodeBeam.UltimateAuth.Sample.UAuthHub.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore.csproj b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore.csproj index 9f0beb00..e15aaf63 100644 --- a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore.csproj +++ b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore/CodeBeam.UltimateAuth.Sample.BlazorServer.EFCore.csproj @@ -9,17 +9,17 @@ - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/CodeBeam.UltimateAuth.Sample.BlazorServer.csproj b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/CodeBeam.UltimateAuth.Sample.BlazorServer.csproj index cfe21912..a582eeab 100644 --- a/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/CodeBeam.UltimateAuth.Sample.BlazorServer.csproj +++ b/samples/blazor-server/CodeBeam.UltimateAuth.Sample.BlazorServer/CodeBeam.UltimateAuth.Sample.BlazorServer.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm.csproj b/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm.csproj index 1ef3e632..07ea416a 100644 --- a/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm.csproj +++ b/samples/blazor-standalone-wasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm/CodeBeam.UltimateAuth.Sample.BlazorStandaloneWasm.csproj @@ -8,10 +8,10 @@ - - - - + + + + diff --git a/samples/int-wasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm.Client/CodeBeam.UAuth.Sample.IntWasm.Client.csproj b/samples/int-wasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm.Client/CodeBeam.UAuth.Sample.IntWasm.Client.csproj index 1dafa9a8..321b6185 100644 --- a/samples/int-wasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm.Client/CodeBeam.UAuth.Sample.IntWasm.Client.csproj +++ b/samples/int-wasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm.Client/CodeBeam.UAuth.Sample.IntWasm.Client.csproj @@ -11,8 +11,8 @@ - - + + diff --git a/samples/int-wasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm.csproj b/samples/int-wasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm.csproj index 2154aec8..2afcbaee 100644 --- a/samples/int-wasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm.csproj +++ b/samples/int-wasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm/CodeBeam.UAuth.Sample.IntWasm.csproj @@ -11,7 +11,7 @@ - + diff --git a/samples/resource-api/CodeBeam.UltimateAuth.Sample.ResourceApi.EfCore/CodeBeam.UltimateAuth.Sample.ResourceApi.EfCore.csproj b/samples/resource-api/CodeBeam.UltimateAuth.Sample.ResourceApi.EfCore/CodeBeam.UltimateAuth.Sample.ResourceApi.EfCore.csproj index cde66c01..3d660c31 100644 --- a/samples/resource-api/CodeBeam.UltimateAuth.Sample.ResourceApi.EfCore/CodeBeam.UltimateAuth.Sample.ResourceApi.EfCore.csproj +++ b/samples/resource-api/CodeBeam.UltimateAuth.Sample.ResourceApi.EfCore/CodeBeam.UltimateAuth.Sample.ResourceApi.EfCore.csproj @@ -8,7 +8,7 @@ - + diff --git a/samples/resource-api/CodeBeam.UltimateAuth.Sample.ResourceApi/CodeBeam.UltimateAuth.Sample.ResourceApi.csproj b/samples/resource-api/CodeBeam.UltimateAuth.Sample.ResourceApi/CodeBeam.UltimateAuth.Sample.ResourceApi.csproj index cde66c01..3d660c31 100644 --- a/samples/resource-api/CodeBeam.UltimateAuth.Sample.ResourceApi/CodeBeam.UltimateAuth.Sample.ResourceApi.csproj +++ b/samples/resource-api/CodeBeam.UltimateAuth.Sample.ResourceApi/CodeBeam.UltimateAuth.Sample.ResourceApi.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/CodeBeam.UltimateAuth.Core/CodeBeam.UltimateAuth.Core.csproj b/src/CodeBeam.UltimateAuth.Core/CodeBeam.UltimateAuth.Core.csproj index c59475f7..f34b9a2a 100644 --- a/src/CodeBeam.UltimateAuth.Core/CodeBeam.UltimateAuth.Core.csproj +++ b/src/CodeBeam.UltimateAuth.Core/CodeBeam.UltimateAuth.Core.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Core diff --git a/src/CodeBeam.UltimateAuth.Server/CodeBeam.UltimateAuth.Server.csproj b/src/CodeBeam.UltimateAuth.Server/CodeBeam.UltimateAuth.Server.csproj index 6f21fab2..6ece6b73 100644 --- a/src/CodeBeam.UltimateAuth.Server/CodeBeam.UltimateAuth.Server.csproj +++ b/src/CodeBeam.UltimateAuth.Server/CodeBeam.UltimateAuth.Server.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Server @@ -22,7 +22,7 @@ - + diff --git a/src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore.csproj b/src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore.csproj index 4437d969..ceab34cf 100644 --- a/src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore.csproj +++ b/src/authentication/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore/CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Authentication.EntityFrameworkCore diff --git a/src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/CodeBeam.UltimateAuth.Authentication.InMemory.csproj b/src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/CodeBeam.UltimateAuth.Authentication.InMemory.csproj index c47f0f33..59cd70f1 100644 --- a/src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/CodeBeam.UltimateAuth.Authentication.InMemory.csproj +++ b/src/authentication/CodeBeam.UltimateAuth.Authentication.InMemory/CodeBeam.UltimateAuth.Authentication.InMemory.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Authentication.InMemory diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/CodeBeam.UltimateAuth.Authorization.Contracts.csproj b/src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/CodeBeam.UltimateAuth.Authorization.Contracts.csproj index 46f73334..7911a019 100644 --- a/src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/CodeBeam.UltimateAuth.Authorization.Contracts.csproj +++ b/src/authorization/CodeBeam.UltimateAuth.Authorization.Contracts/CodeBeam.UltimateAuth.Authorization.Contracts.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Authorization.Contracts diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore.csproj b/src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore.csproj index 76cfca96..f85a3966 100644 --- a/src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore.csproj +++ b/src/authorization/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore/CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Authorization.EntityFrameworkCore diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/CodeBeam.UltimateAuth.Authorization.InMemory.csproj b/src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/CodeBeam.UltimateAuth.Authorization.InMemory.csproj index a7723fcd..627076aa 100644 --- a/src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/CodeBeam.UltimateAuth.Authorization.InMemory.csproj +++ b/src/authorization/CodeBeam.UltimateAuth.Authorization.InMemory/CodeBeam.UltimateAuth.Authorization.InMemory.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Authorization.InMemory diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/CodeBeam.UltimateAuth.Authorization.Reference.csproj b/src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/CodeBeam.UltimateAuth.Authorization.Reference.csproj index 82976ca3..38c3e64f 100644 --- a/src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/CodeBeam.UltimateAuth.Authorization.Reference.csproj +++ b/src/authorization/CodeBeam.UltimateAuth.Authorization.Reference/CodeBeam.UltimateAuth.Authorization.Reference.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Authorization.Reference diff --git a/src/authorization/CodeBeam.UltimateAuth.Authorization/CodeBeam.UltimateAuth.Authorization.csproj b/src/authorization/CodeBeam.UltimateAuth.Authorization/CodeBeam.UltimateAuth.Authorization.csproj index 00dad5a1..d83ec60d 100644 --- a/src/authorization/CodeBeam.UltimateAuth.Authorization/CodeBeam.UltimateAuth.Authorization.csproj +++ b/src/authorization/CodeBeam.UltimateAuth.Authorization/CodeBeam.UltimateAuth.Authorization.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Authorization diff --git a/src/bundle/CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle/CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle.csproj b/src/bundle/CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle/CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle.csproj index 17220e06..9926c359 100644 --- a/src/bundle/CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle/CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle.csproj +++ b/src/bundle/CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle/CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.EntityFrameworkCore.Bundle diff --git a/src/bundle/CodeBeam.UltimateAuth.InMemory.Bundle/CodeBeam.UltimateAuth.InMemory.Bundle.csproj b/src/bundle/CodeBeam.UltimateAuth.InMemory.Bundle/CodeBeam.UltimateAuth.InMemory.Bundle.csproj index c0c22ad8..a64f2137 100644 --- a/src/bundle/CodeBeam.UltimateAuth.InMemory.Bundle/CodeBeam.UltimateAuth.InMemory.Bundle.csproj +++ b/src/bundle/CodeBeam.UltimateAuth.InMemory.Bundle/CodeBeam.UltimateAuth.InMemory.Bundle.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.InMemory.Bundle diff --git a/src/bundle/CodeBeam.UltimateAuth.Reference.Bundle/CodeBeam.UltimateAuth.Reference.Bundle.csproj b/src/bundle/CodeBeam.UltimateAuth.Reference.Bundle/CodeBeam.UltimateAuth.Reference.Bundle.csproj index 01f6356a..48919bb1 100644 --- a/src/bundle/CodeBeam.UltimateAuth.Reference.Bundle/CodeBeam.UltimateAuth.Reference.Bundle.csproj +++ b/src/bundle/CodeBeam.UltimateAuth.Reference.Bundle/CodeBeam.UltimateAuth.Reference.Bundle.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Reference.Bundle diff --git a/src/client/CodeBeam.UltimateAuth.Client.AspNetCore/CodeBeam.UltimateAuth.Client.AspNetCore.csproj b/src/client/CodeBeam.UltimateAuth.Client.AspNetCore/CodeBeam.UltimateAuth.Client.AspNetCore.csproj index a6b3ab90..fcb04456 100644 --- a/src/client/CodeBeam.UltimateAuth.Client.AspNetCore/CodeBeam.UltimateAuth.Client.AspNetCore.csproj +++ b/src/client/CodeBeam.UltimateAuth.Client.AspNetCore/CodeBeam.UltimateAuth.Client.AspNetCore.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Client.AspNetCore diff --git a/src/client/CodeBeam.UltimateAuth.Client.Blazor/CodeBeam.UltimateAuth.Client.Blazor.csproj b/src/client/CodeBeam.UltimateAuth.Client.Blazor/CodeBeam.UltimateAuth.Client.Blazor.csproj index 73d5c800..a2e8969e 100644 --- a/src/client/CodeBeam.UltimateAuth.Client.Blazor/CodeBeam.UltimateAuth.Client.Blazor.csproj +++ b/src/client/CodeBeam.UltimateAuth.Client.Blazor/CodeBeam.UltimateAuth.Client.Blazor.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) CodeBeam.UltimateAuth.Client.Blazor @@ -16,25 +16,25 @@ - - - - + + + + - - - - + + + + - - - - + + + + - + diff --git a/src/client/CodeBeam.UltimateAuth.Client.JsMinifier/CodeBeam.UltimateAuth.Client.JsMinifier.csproj b/src/client/CodeBeam.UltimateAuth.Client.JsMinifier/CodeBeam.UltimateAuth.Client.JsMinifier.csproj index cdd30716..bc6e3e8f 100644 --- a/src/client/CodeBeam.UltimateAuth.Client.JsMinifier/CodeBeam.UltimateAuth.Client.JsMinifier.csproj +++ b/src/client/CodeBeam.UltimateAuth.Client.JsMinifier/CodeBeam.UltimateAuth.Client.JsMinifier.csproj @@ -7,7 +7,7 @@ - + diff --git a/src/client/CodeBeam.UltimateAuth.Client/CodeBeam.UltimateAuth.Client.csproj b/src/client/CodeBeam.UltimateAuth.Client/CodeBeam.UltimateAuth.Client.csproj index f7f329c1..7f13e4e3 100644 --- a/src/client/CodeBeam.UltimateAuth.Client/CodeBeam.UltimateAuth.Client.csproj +++ b/src/client/CodeBeam.UltimateAuth.Client/CodeBeam.UltimateAuth.Client.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) CodeBeam.UltimateAuth.Client diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/CodeBeam.UltimateAuth.Credentials.Contracts.csproj b/src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/CodeBeam.UltimateAuth.Credentials.Contracts.csproj index 376e6b7a..1186eb80 100644 --- a/src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/CodeBeam.UltimateAuth.Credentials.Contracts.csproj +++ b/src/credentials/CodeBeam.UltimateAuth.Credentials.Contracts/CodeBeam.UltimateAuth.Credentials.Contracts.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Credentials.Contracts diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore.csproj b/src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore.csproj index 978ce78c..92f06217 100644 --- a/src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore.csproj +++ b/src/credentials/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore/CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Credentials.EntityFrameworkCore diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/CodeBeam.UltimateAuth.Credentials.InMemory.csproj b/src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/CodeBeam.UltimateAuth.Credentials.InMemory.csproj index 7bc21c6a..0351770a 100644 --- a/src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/CodeBeam.UltimateAuth.Credentials.InMemory.csproj +++ b/src/credentials/CodeBeam.UltimateAuth.Credentials.InMemory/CodeBeam.UltimateAuth.Credentials.InMemory.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Credentials.InMemory diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/CodeBeam.UltimateAuth.Credentials.Reference.csproj b/src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/CodeBeam.UltimateAuth.Credentials.Reference.csproj index ec99e9ab..2656c92f 100644 --- a/src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/CodeBeam.UltimateAuth.Credentials.Reference.csproj +++ b/src/credentials/CodeBeam.UltimateAuth.Credentials.Reference/CodeBeam.UltimateAuth.Credentials.Reference.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Credentials.Reference diff --git a/src/credentials/CodeBeam.UltimateAuth.Credentials/CodeBeam.UltimateAuth.Credentials.csproj b/src/credentials/CodeBeam.UltimateAuth.Credentials/CodeBeam.UltimateAuth.Credentials.csproj index 53803f40..83ac2e7a 100644 --- a/src/credentials/CodeBeam.UltimateAuth.Credentials/CodeBeam.UltimateAuth.Credentials.csproj +++ b/src/credentials/CodeBeam.UltimateAuth.Credentials/CodeBeam.UltimateAuth.Credentials.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Credentials diff --git a/src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/CodeBeam.UltimateAuth.EntityFrameworkCore.csproj b/src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/CodeBeam.UltimateAuth.EntityFrameworkCore.csproj index 11ada993..cf84ac1b 100644 --- a/src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/CodeBeam.UltimateAuth.EntityFrameworkCore.csproj +++ b/src/persistence/CodeBeam.UltimateAuth.EntityFrameworkCore/CodeBeam.UltimateAuth.EntityFrameworkCore.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.EntityFrameworkCore @@ -18,16 +18,16 @@ - - + + - - + + - - + + diff --git a/src/persistence/CodeBeam.UltimateAuth.InMemory/CodeBeam.UltimateAuth.InMemory.csproj b/src/persistence/CodeBeam.UltimateAuth.InMemory/CodeBeam.UltimateAuth.InMemory.csproj index ca100aa8..8ef46540 100644 --- a/src/persistence/CodeBeam.UltimateAuth.InMemory/CodeBeam.UltimateAuth.InMemory.csproj +++ b/src/persistence/CodeBeam.UltimateAuth.InMemory/CodeBeam.UltimateAuth.InMemory.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.InMemory diff --git a/src/policies/CodeBeam.UltimateAuth.Policies/CodeBeam.UltimateAuth.Policies.csproj b/src/policies/CodeBeam.UltimateAuth.Policies/CodeBeam.UltimateAuth.Policies.csproj index 6f703658..13c2f88f 100644 --- a/src/policies/CodeBeam.UltimateAuth.Policies/CodeBeam.UltimateAuth.Policies.csproj +++ b/src/policies/CodeBeam.UltimateAuth.Policies/CodeBeam.UltimateAuth.Policies.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Policies diff --git a/src/security/CodeBeam.UltimateAuth.Security.Argon2/CodeBeam.UltimateAuth.Security.Argon2.csproj b/src/security/CodeBeam.UltimateAuth.Security.Argon2/CodeBeam.UltimateAuth.Security.Argon2.csproj index 1ed21519..d7b51946 100644 --- a/src/security/CodeBeam.UltimateAuth.Security.Argon2/CodeBeam.UltimateAuth.Security.Argon2.csproj +++ b/src/security/CodeBeam.UltimateAuth.Security.Argon2/CodeBeam.UltimateAuth.Security.Argon2.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Security.Argon2 diff --git a/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore.csproj b/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore.csproj index 345afa17..69308198 100644 --- a/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore.csproj +++ b/src/sessions/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore/CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Sessions.EntityFrameworkCore diff --git a/src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/CodeBeam.UltimateAuth.Sessions.InMemory.csproj b/src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/CodeBeam.UltimateAuth.Sessions.InMemory.csproj index 09a0ea22..e2a8e517 100644 --- a/src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/CodeBeam.UltimateAuth.Sessions.InMemory.csproj +++ b/src/sessions/CodeBeam.UltimateAuth.Sessions.InMemory/CodeBeam.UltimateAuth.Sessions.InMemory.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Sessions.InMemory diff --git a/src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore.csproj b/src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore.csproj index 6d1c5e19..cb3f075e 100644 --- a/src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore.csproj +++ b/src/tokens/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore/CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Tokens.EntityFrameworkCore diff --git a/src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/CodeBeam.UltimateAuth.Tokens.InMemory.csproj b/src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/CodeBeam.UltimateAuth.Tokens.InMemory.csproj index 4be63678..708c17cd 100644 --- a/src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/CodeBeam.UltimateAuth.Tokens.InMemory.csproj +++ b/src/tokens/CodeBeam.UltimateAuth.Tokens.InMemory/CodeBeam.UltimateAuth.Tokens.InMemory.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Tokens.InMemory diff --git a/src/users/CodeBeam.UltimateAuth.Users.Contracts/CodeBeam.UltimateAuth.Users.Contracts.csproj b/src/users/CodeBeam.UltimateAuth.Users.Contracts/CodeBeam.UltimateAuth.Users.Contracts.csproj index 8ff8aa91..3bcc4e6e 100644 --- a/src/users/CodeBeam.UltimateAuth.Users.Contracts/CodeBeam.UltimateAuth.Users.Contracts.csproj +++ b/src/users/CodeBeam.UltimateAuth.Users.Contracts/CodeBeam.UltimateAuth.Users.Contracts.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Users.Contracts diff --git a/src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/CodeBeam.UltimateAuth.Users.EntityFrameworkCore.csproj b/src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/CodeBeam.UltimateAuth.Users.EntityFrameworkCore.csproj index ab0912de..6ffa4e97 100644 --- a/src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/CodeBeam.UltimateAuth.Users.EntityFrameworkCore.csproj +++ b/src/users/CodeBeam.UltimateAuth.Users.EntityFrameworkCore/CodeBeam.UltimateAuth.Users.EntityFrameworkCore.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Users.EntityFrameworkCore diff --git a/src/users/CodeBeam.UltimateAuth.Users.InMemory/CodeBeam.UltimateAuth.Users.InMemory.csproj b/src/users/CodeBeam.UltimateAuth.Users.InMemory/CodeBeam.UltimateAuth.Users.InMemory.csproj index 0d7ed5fb..8581ea8a 100644 --- a/src/users/CodeBeam.UltimateAuth.Users.InMemory/CodeBeam.UltimateAuth.Users.InMemory.csproj +++ b/src/users/CodeBeam.UltimateAuth.Users.InMemory/CodeBeam.UltimateAuth.Users.InMemory.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Users.InMemory diff --git a/src/users/CodeBeam.UltimateAuth.Users.Reference/CodeBeam.UltimateAuth.Users.Reference.csproj b/src/users/CodeBeam.UltimateAuth.Users.Reference/CodeBeam.UltimateAuth.Users.Reference.csproj index 3b4b5061..db838103 100644 --- a/src/users/CodeBeam.UltimateAuth.Users.Reference/CodeBeam.UltimateAuth.Users.Reference.csproj +++ b/src/users/CodeBeam.UltimateAuth.Users.Reference/CodeBeam.UltimateAuth.Users.Reference.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Users.Reference diff --git a/src/users/CodeBeam.UltimateAuth.Users/CodeBeam.UltimateAuth.Users.csproj b/src/users/CodeBeam.UltimateAuth.Users/CodeBeam.UltimateAuth.Users.csproj index 9dd91208..4a454f08 100644 --- a/src/users/CodeBeam.UltimateAuth.Users/CodeBeam.UltimateAuth.Users.csproj +++ b/src/users/CodeBeam.UltimateAuth.Users/CodeBeam.UltimateAuth.Users.csproj @@ -1,7 +1,7 @@  - net8.0;net9.0;net10.0 + $(UltimateAuthBuildTargetFrameworks) $(NoWarn);1591 CodeBeam.UltimateAuth.Users diff --git a/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/CodeBeam.UltimateAuth.DocsBuilder.csproj b/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/CodeBeam.UltimateAuth.DocsBuilder.csproj index cdce659b..bcef26b9 100644 --- a/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/CodeBeam.UltimateAuth.DocsBuilder.csproj +++ b/src/utilities/CodeBeam.UltimateAuth.DocsBuilder/CodeBeam.UltimateAuth.DocsBuilder.csproj @@ -9,8 +9,8 @@ - - + + diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/AssemblyBehavior.cs b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/AssemblyBehavior.cs index c7fc3b1f..0378b5a6 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/AssemblyBehavior.cs +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/AssemblyBehavior.cs @@ -1 +1,3 @@ -[assembly: CollectionBehavior(DisableTestParallelization = true)] +using Xunit.v3; + +[assembly: Parallelization(Mode = Xunit.Sdk.ParallelMode.None)] diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Authorization/SandboxPolicyTests.cs b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Authorization/SandboxPolicyTests.cs index 818b4c05..91aef379 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Authorization/SandboxPolicyTests.cs +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Authorization/SandboxPolicyTests.cs @@ -26,11 +26,11 @@ public async Task Owner_cannot_access_foreign_or_missing_targets(string action) foreach (var target in new UserKey?[] { LabDatabase.MemberB, LabDatabase.OwnerB, UserKey.FromString("nonexistent"), null }) { - var denied = await policy.DecideAsync(Contexts.Create(action, LabDatabase.OwnerA, target)); + var denied = await policy.DecideAsync(Contexts.Create(action, LabDatabase.OwnerA, target), TestContext.Current.CancellationToken); Assert.True(denied.IsDenied); } Assert.True((await policy.DecideAsync(Contexts.Create(action, - LabDatabase.OwnerA, LabDatabase.MemberA))).IsAllowed); + LabDatabase.OwnerA, LabDatabase.MemberA), TestContext.Current.CancellationToken)).IsAllowed); Assert.Equal(before, await db.SnapshotAsync()); } @@ -44,9 +44,9 @@ public async Task Inactive_sandbox_blocks_writes_and_suspension_also_blocks_read await db.SetStatusAsync(status); var policy = new SandboxUAuthAccessPolicy(db, new LabRootResolver(), new()); Assert.True((await policy.DecideAsync(Contexts.Create(UAuthActions.UserProfiles.UpdateAdmin, - LabDatabase.OwnerA, LabDatabase.MemberA))).IsDenied); + LabDatabase.OwnerA, LabDatabase.MemberA), TestContext.Current.CancellationToken)).IsDenied); Assert.Equal(readDenied, (await policy.DecideAsync(Contexts.Create(UAuthActions.UserProfiles.GetAdmin, - LabDatabase.OwnerA, LabDatabase.MemberA))).IsDenied); + LabDatabase.OwnerA, LabDatabase.MemberA), TestContext.Current.CancellationToken)).IsDenied); } [Fact] @@ -56,11 +56,11 @@ public async Task Members_cannot_manage_users_and_owners_cannot_be_deleted() await db.SeedAsync(); var policy = new SandboxUAuthAccessPolicy(db, new LabRootResolver(), new()); Assert.Equal("sandbox_owner_required", (await policy.DecideAsync(Contexts.Create( - UAuthActions.Users.DeleteAdmin, LabDatabase.MemberA, LabDatabase.OwnerA))).DenyReason); + UAuthActions.Users.DeleteAdmin, LabDatabase.MemberA, LabDatabase.OwnerA), TestContext.Current.CancellationToken)).DenyReason); Assert.Equal("sandbox_owner_cannot_be_deleted", (await policy.DecideAsync(Contexts.Create( - UAuthActions.Users.DeleteAdmin, LabDatabase.OwnerA, LabDatabase.OwnerA))).DenyReason); + UAuthActions.Users.DeleteAdmin, LabDatabase.OwnerA, LabDatabase.OwnerA), TestContext.Current.CancellationToken)).DenyReason); Assert.Equal("sandbox_owner_cannot_delete_self", (await policy.DecideAsync(Contexts.Create( - UAuthActions.Users.DeleteSelf, LabDatabase.OwnerA, LabDatabase.OwnerA))).DenyReason); + UAuthActions.Users.DeleteSelf, LabDatabase.OwnerA, LabDatabase.OwnerA), TestContext.Current.CancellationToken)).DenyReason); } [Fact] @@ -71,8 +71,8 @@ public async Task Batch_read_checks_every_target_and_requires_nonempty_targets() var policy = new SandboxUAuthAccessPolicy(db, new LabRootResolver(), new()); foreach (var targets in new[] { Array.Empty(), new[] { LabDatabase.MemberA, LabDatabase.MemberB } }) Assert.True((await policy.DecideAsync(Contexts.Create(UAuthActions.Users.GetByKeysAdmin, - LabDatabase.OwnerA, targets: targets))).IsDenied); + LabDatabase.OwnerA, targets: targets), TestContext.Current.CancellationToken)).IsDenied); Assert.True((await policy.DecideAsync(Contexts.Create(UAuthActions.Users.GetByKeysAdmin, - LabDatabase.OwnerA, targets: new[] { LabDatabase.MemberA, LabDatabase.OwnerA }))).IsAllowed); + LabDatabase.OwnerA, targets: new[] { LabDatabase.MemberA, LabDatabase.OwnerA }), TestContext.Current.CancellationToken)).IsAllowed); } } diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/CodeBeam.UltimateAuth.IdentityLab.Tests.csproj b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/CodeBeam.UltimateAuth.IdentityLab.Tests.csproj index 85ebd04e..7f863920 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/CodeBeam.UltimateAuth.IdentityLab.Tests.csproj +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/CodeBeam.UltimateAuth.IdentityLab.Tests.csproj @@ -1,20 +1,16 @@  - net10.0 false true - - - - - - - - - + + + + + + diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Infrastructure/PostgresFixture.cs b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Infrastructure/PostgresFixture.cs index bf45c506..3f2936e0 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Infrastructure/PostgresFixture.cs +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Infrastructure/PostgresFixture.cs @@ -12,7 +12,7 @@ public sealed class PostgresFixture : IAsyncLifetime { private PostgreSqlContainer? container; private string connectionString = ""; - public async Task InitializeAsync() + public async ValueTask InitializeAsync() { connectionString = Environment.GetEnvironmentVariable("IDENTITYLAB_TEST_POSTGRES") ?? ""; if (string.IsNullOrWhiteSpace(connectionString)) @@ -36,7 +36,7 @@ public async Task CreateAsync() await db.Database.MigrateAsync(); return database; } - public async Task DisposeAsync() + public async ValueTask DisposeAsync() { if (container is not null) await container.DisposeAsync(); } diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Integration/RegistrationIntegrationTests.cs b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Integration/RegistrationIntegrationTests.cs index 20a9b7d7..67f6757d 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Integration/RegistrationIntegrationTests.cs +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Integration/RegistrationIntegrationTests.cs @@ -26,33 +26,33 @@ public async Task Registration_persists_account_owner_role_and_sandbox_without_l var registration = new LabRegistrationService(ActorProvider.For(null), sp.GetRequiredService(), sp.GetRequiredService(), sp.GetRequiredService(), new(db), new(db, NullLogger.Instance)); - var result = await registration.RegisterAsync(new("alice", "alice@example.test", "test-password-8!")); + var result = await registration.RegisterAsync(new("alice", "alice@example.test", "test-password-8!"), TestContext.Current.CancellationToken); Assert.True(result.IsSuccess, result.ErrorMessage); UserKey owner; await using (var verify = db.CreateDbContext()) { - var sandbox = await verify.Sandboxes.Include(x => x.Members).SingleAsync(); + var sandbox = await verify.Sandboxes.Include(x => x.Members).SingleAsync(TestContext.Current.CancellationToken); owner = sandbox.OwnerUserKey; Assert.Equal(owner, Assert.Single(sandbox.Members).UserKey); } // Query actual framework stores, using a new scope to avoid tracked state. await using var verification = runtime.Services.CreateAsyncScope(); var assignments = await verification.ServiceProvider.GetRequiredService() - .Create(TenantKeys.Single).GetAssignmentsAsync(owner); + .Create(TenantKeys.Single).GetAssignmentsAsync(owner, TestContext.Current.CancellationToken); Assert.Contains(assignments, x => x.RoleId == LabRoleIds.Owner); Assert.DoesNotContain(assignments, x => x.RoleId == LabRoleIds.Admin); var lifecycle = await verification.ServiceProvider.GetRequiredService() - .Create(TenantKeys.Single).GetAsync(new UserLifecycleKey(TenantKeys.Single, owner)); + .Create(TenantKeys.Single).GetAsync(new UserLifecycleKey(TenantKeys.Single, owner), TestContext.Current.CancellationToken); Assert.NotNull(lifecycle); var duplicate = await Record.ExceptionAsync(async () => { - var duplicateResult = await registration.RegisterAsync(new("alice", "alice@example.test", "test-password-8!")); + var duplicateResult = await registration.RegisterAsync(new("alice", "alice@example.test", "test-password-8!"), TestContext.Current.CancellationToken); Assert.False(duplicateResult.IsSuccess); }); // Duplicate validation may throw or return failure depending on the framework contract. if (duplicate is not null) Assert.IsType(duplicate); await using var final = db.CreateDbContext(); - Assert.Equal(1, await final.Sandboxes.CountAsync()); + Assert.Equal(1, await final.Sandboxes.CountAsync(TestContext.Current.CancellationToken)); } [Fact] @@ -62,14 +62,14 @@ public async Task Trusted_seed_is_repeatable_and_assigns_ladmin_only_to_fixed_ad await using var runtime = await LabRuntime.CreateAsync(db); await using var scope = runtime.Services.CreateAsyncScope(); var sp = scope.ServiceProvider; - await ActivatorUtilities.CreateInstance(sp).SeedAsync(TenantKeys.Single); - await ActivatorUtilities.CreateInstance(sp).SeedAsync(TenantKeys.Single); + await ActivatorUtilities.CreateInstance(sp).SeedAsync(TenantKeys.Single, TestContext.Current.CancellationToken); + await ActivatorUtilities.CreateInstance(sp).SeedAsync(TenantKeys.Single, TestContext.Current.CancellationToken); var store = sp.GetRequiredService().Create(TenantKeys.Single); - var assignments = await store.GetAssignmentsAsync(LabAdminIdentity.UserKey); + var assignments = await store.GetAssignmentsAsync(LabAdminIdentity.UserKey, TestContext.Current.CancellationToken); Assert.Single(assignments, x => x.RoleId == LabRoleIds.Admin); - Assert.Equal(1, await store.CountAssignmentsAsync(LabRoleIds.Admin)); + Assert.Equal(1, await store.CountAssignmentsAsync(LabRoleIds.Admin, TestContext.Current.CancellationToken)); var roleStore = sp.GetRequiredService().Create(TenantKeys.Single); - var manager = await roleStore.GetByNameAsync("MANAGER"); + var manager = await roleStore.GetByNameAsync("MANAGER", TestContext.Current.CancellationToken); Assert.NotNull(manager); Assert.Equal(LabRoleIds.Manager, manager.Id); Assert.True(manager.Permissions.Select(x => x.Value).ToHashSet().SetEquals(LabDemoPermissions.Manager)); @@ -85,14 +85,14 @@ public async Task Actual_role_service_denies_user_escalation_without_changing_pe await using var scope = runtime.Services.CreateAsyncScope(); var service = scope.ServiceProvider.GetRequiredService(); var store = scope.ServiceProvider.GetRequiredService().Create(TenantKeys.Single); - var before = await store.GetAssignmentsAsync(LabDatabase.MemberA); + var before = await store.GetAssignmentsAsync(LabDatabase.MemberA, TestContext.Current.CancellationToken); var context = Contexts.Create(CodeBeam.UltimateAuth.Core.Defaults.UAuthActions.Authorization.Roles.AssignAdmin, LabDatabase.MemberA, LabDatabase.MemberA); await Assert.ThrowsAsync(() => - service.AssignAsync(context, LabDatabase.MemberA, role)); + service.AssignAsync(context, LabDatabase.MemberA, role, TestContext.Current.CancellationToken)); await using var verification = runtime.Services.CreateAsyncScope(); var after = await verification.ServiceProvider.GetRequiredService() - .Create(TenantKeys.Single).GetAssignmentsAsync(LabDatabase.MemberA); + .Create(TenantKeys.Single).GetAssignmentsAsync(LabDatabase.MemberA, TestContext.Current.CancellationToken); Assert.Equal(before.Select(x => x.RoleId).OrderBy(x => x.Value), after.Select(x => x.RoleId).OrderBy(x => x.Value)); } @@ -108,31 +108,31 @@ public async Task Owner_can_provision_member_and_assign_demo_role_but_cannot_del var assignments = sp.GetRequiredService(); var registration = new LabRegistrationService(ActorProvider.For(null), users, roles, assignments, new(db), new(db, NullLogger.Instance)); - Assert.True((await registration.RegisterAsync(new("owner", "owner@example.test", "test-password-8!"))).IsSuccess); + Assert.True((await registration.RegisterAsync(new("owner", "owner@example.test", "test-password-8!"), TestContext.Current.CancellationToken)).IsSuccess); UserKey owner; - await using (var verify = db.CreateDbContext()) owner = (await verify.Sandboxes.SingleAsync()).OwnerUserKey; + await using (var verify = db.CreateDbContext()) owner = (await verify.Sandboxes.SingleAsync(TestContext.Current.CancellationToken)).OwnerUserKey; var provider = ActorProvider.For(owner); var provisioning = new LabUserProvisioningService(provider, new(db), new(db), users, roles, assignments, sp.GetRequiredService(), NullLogger.Instance); var result = await provisioning.CreateMemberAsync(owner, new CreateUserRequest - { UserName = "member", Email = "member@example.test", Password = "test-password-8!" }); + { UserName = "member", Email = "member@example.test", Password = "test-password-8!" }, TestContext.Current.CancellationToken); Assert.True(result.IsSuccess, result.FailureReason); var member = UserKey.FromString(result.UserKey!); var demos = new LabDemoRoleService(provider, db, roles, assignments); - await demos.SetRoleAsync(member, "Manager", true); - await Assert.ThrowsAsync(() => demos.SetRoleAsync(member, "Ladmin", true)); + await demos.SetRoleAsync(member, "Manager", true, TestContext.Current.CancellationToken); + await Assert.ThrowsAsync(() => demos.SetRoleAsync(member, "Ladmin", true, ct: TestContext.Current.CancellationToken)); var roleService = sp.GetRequiredService(); await Assert.ThrowsAsync(() => roleService.AssignAsync(Contexts.Create(CodeBeam.UltimateAuth.Core.Defaults.UAuthActions.Authorization.Roles.AssignAdmin, - owner, member), member, "Ladmin")); + owner, member), member, "Ladmin", TestContext.Current.CancellationToken)); await using var fresh = runtime.Services.CreateAsyncScope(); var persisted = await fresh.ServiceProvider.GetRequiredService() - .Create(TenantKeys.Single).GetAssignmentsAsync(member); + .Create(TenantKeys.Single).GetAssignmentsAsync(member, TestContext.Current.CancellationToken); Assert.Contains(persisted, x => x.RoleId == LabRoleIds.User); Assert.Contains(persisted, x => x.RoleId == LabRoleIds.Manager); Assert.DoesNotContain(persisted, x => x.RoleId == LabRoleIds.Admin || x.RoleId == LabRoleIds.Owner); await using var final = db.CreateDbContext(); - Assert.Equal(member, (await final.SandboxMembers.SingleAsync(x => x.UserKey == member)).UserKey); + Assert.Equal(member, (await final.SandboxMembers.SingleAsync(x => x.UserKey == member, TestContext.Current.CancellationToken)).UserKey); } [Fact] @@ -144,21 +144,21 @@ public async Task Owner_cannot_rename_delete_or_change_permissions_of_system_rol await using var scope = runtime.Services.CreateAsyncScope(); var roles = scope.ServiceProvider.GetRequiredService(); var store = scope.ServiceProvider.GetRequiredService().Create(TenantKeys.Single); - var before = await store.GetByNameAsync("LADMIN"); + var before = await store.GetByNameAsync("LADMIN", TestContext.Current.CancellationToken); Assert.NotNull(before); CodeBeam.UltimateAuth.Core.Contracts.AccessContext Context(string action) => Contexts.Create(action, LabDatabase.OwnerA); await Assert.ThrowsAsync(() => - roles.RenameAsync(Context("authorization.roles.rename.admin"), LabRoleIds.Admin, "Manager")); + roles.RenameAsync(Context("authorization.roles.rename.admin"), LabRoleIds.Admin, "Manager", TestContext.Current.CancellationToken)); await Assert.ThrowsAsync(() => roles.DeleteAsync(Context("authorization.roles.delete.admin"), LabRoleIds.Admin, - CodeBeam.UltimateAuth.Core.Contracts.DeleteMode.Hard)); + CodeBeam.UltimateAuth.Core.Contracts.DeleteMode.Hard, TestContext.Current.CancellationToken)); await Assert.ThrowsAsync(() => roles.SetPermissionsAsync(Context("authorization.roles.permissions.admin"), LabRoleIds.Admin, - Array.Empty())); + Array.Empty(), TestContext.Current.CancellationToken)); await using var fresh = runtime.Services.CreateAsyncScope(); var after = await fresh.ServiceProvider.GetRequiredService() - .Create(TenantKeys.Single).GetByNameAsync("LADMIN"); + .Create(TenantKeys.Single).GetByNameAsync("LADMIN", TestContext.Current.CancellationToken); Assert.NotNull(after); Assert.Equal(before.Id, after.Id); Assert.Equal(before.Version, after.Version); @@ -179,9 +179,9 @@ public async Task Provisioning_assignment_fault_hard_deletes_real_partial_accoun var assignments = sp.GetRequiredService(); var registration = new LabRegistrationService(ActorProvider.For(null), users, roles, assignments, new(db), new(db, NullLogger.Instance)); - Assert.True((await registration.RegisterAsync(new("owner", "owner@example.test", "test-password-8!"))).IsSuccess); + Assert.True((await registration.RegisterAsync(new("owner", "owner@example.test", "test-password-8!"), TestContext.Current.CancellationToken)).IsSuccess); UserKey owner; - await using (var verify = db.CreateDbContext()) owner = (await verify.Sandboxes.SingleAsync()).OwnerUserKey; + await using (var verify = db.CreateDbContext()) owner = (await verify.Sandboxes.SingleAsync(cancellationToken: TestContext.Current.CancellationToken)).OwnerUserKey; var before = await db.SnapshotAsync(); var failedStore = new Moq.Mock(); failedStore.Setup(x => x.GetAssignmentsAsync(Moq.It.IsAny(), Moq.It.IsAny())) @@ -192,14 +192,14 @@ public async Task Provisioning_assignment_fault_hard_deletes_real_partial_accoun roles, failedFactory.Object, sp.GetRequiredService(), NullLogger.Instance); var failure = await Assert.ThrowsAsync(() => provisioning.CreateMemberAsync(owner, - new CreateUserRequest { UserName = "failed-member", Email = "failed-member@example.test", Password = "Member-test-password-8!" })); + new CreateUserRequest { UserName = "failed-member", Email = "failed-member@example.test", Password = "Member-test-password-8!" }, TestContext.Current.CancellationToken)); var partialKey = UserKey.FromString(failure.CreatedUserKey); await using var fresh = runtime.Services.CreateAsyncScope(); var lifecycle = await fresh.ServiceProvider.GetRequiredService() - .Create(TenantKeys.Single).GetAsync(new UserLifecycleKey(TenantKeys.Single, partialKey)); + .Create(TenantKeys.Single).GetAsync(new UserLifecycleKey(TenantKeys.Single, partialKey), TestContext.Current.CancellationToken); Assert.Null(lifecycle); var remainingRoles = await fresh.ServiceProvider.GetRequiredService() - .Create(TenantKeys.Single).GetAssignmentsAsync(partialKey); + .Create(TenantKeys.Single).GetAssignmentsAsync(partialKey, TestContext.Current.CancellationToken); Assert.Empty(remainingRoles); Assert.Equal(before, await db.SnapshotAsync()); } @@ -213,19 +213,18 @@ public async Task Actual_context_factory_replaces_forged_ladmin_attributes_with_ await using var scope = runtime.Services.CreateAsyncScope(); var sp = scope.ServiceProvider; await new LabRoleService(sp.GetRequiredService(), sp.GetRequiredService()) - .AssignAsync(TenantKeys.Single, LabDatabase.OwnerA, "Owner"); + .AssignAsync(TenantKeys.Single, LabDatabase.OwnerA, "Owner", TestContext.Current.CancellationToken); var context = await sp.GetRequiredService() .CreateAsync(Contexts.AuthenticatedFlow(LabDatabase.OwnerA), "users.delete.admin", "users", - LabDatabase.MemberB.Value, new Dictionary { ["roles"] = new[] { LabRoleIds.Admin } }); + LabDatabase.MemberB.Value, new Dictionary { ["roles"] = new[] { LabRoleIds.Admin } }, TestContext.Current.CancellationToken); Assert.False(new LabRootResolver().IsLadmin(context)); Assert.Equal(LabDatabase.OwnerA, context.ActorUserKey); var resolved = Assert.IsAssignableFrom>(context.Attributes["roles"]); Assert.Contains(LabRoleIds.Owner, resolved); Assert.DoesNotContain(LabRoleIds.Admin, resolved); var decision = await new CodeBeam.UltimateAuth.IdentityLab.Policy.SandboxUAuthAccessPolicy(db, - new LabRootResolver(), sp.GetRequiredService()).DecideAsync(context); + new LabRootResolver(), sp.GetRequiredService()).DecideAsync(context, TestContext.Current.CancellationToken); Assert.True(decision.IsDenied); Assert.Equal("sandbox_target_out_of_scope", decision.DenyReason); } - } diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Integration/SandboxPersistenceTests.cs b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Integration/SandboxPersistenceTests.cs index 982ff58a..f84054aa 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Integration/SandboxPersistenceTests.cs +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Integration/SandboxPersistenceTests.cs @@ -11,9 +11,9 @@ public sealed class SandboxPersistenceTests(PostgresFixture postgres) : IClassFi public async Task Creation_persists_owner_membership_and_settings_together() { await using var db = await postgres.CreateAsync(); - var sandbox = await new SandboxService(db).CreateAsync(LabDatabase.OwnerA, " Sandbox A "); + var sandbox = await new SandboxService(db).CreateAsync(LabDatabase.OwnerA, " Sandbox A ", TestContext.Current.CancellationToken); await using var verify = db.CreateDbContext(); - var actual = await verify.Sandboxes.Include(x => x.Members).Include(x => x.Settings).SingleAsync(); + var actual = await verify.Sandboxes.Include(x => x.Members).Include(x => x.Settings).SingleAsync(cancellationToken: TestContext.Current.CancellationToken); Assert.Equal(sandbox.Id, actual.Id); Assert.Equal("Sandbox A", actual.Name); Assert.Equal(LabDatabase.OwnerA, actual.OwnerUserKey); @@ -30,14 +30,14 @@ public async Task Cross_sandbox_member_removal_and_owner_removal_leave_database_ await db.SeedAsync(); var before = await db.SnapshotAsync(); var service = new SandboxService(db); - Assert.False(await service.RemoveMemberAsync(LabDatabase.OwnerA, LabDatabase.MemberB)); - await Assert.ThrowsAsync(() => service.RemoveMemberAsync(LabDatabase.OwnerA, LabDatabase.OwnerA)); - await Assert.ThrowsAsync(() => service.RemoveMemberAsync(LabDatabase.MemberA, LabDatabase.MemberA)); + Assert.False(await service.RemoveMemberAsync(LabDatabase.OwnerA, LabDatabase.MemberB, TestContext.Current.CancellationToken)); + await Assert.ThrowsAsync(() => service.RemoveMemberAsync(LabDatabase.OwnerA, LabDatabase.OwnerA, TestContext.Current.CancellationToken)); + await Assert.ThrowsAsync(() => service.RemoveMemberAsync(LabDatabase.MemberA, LabDatabase.MemberA, TestContext.Current.CancellationToken)); Assert.Equal(before, await db.SnapshotAsync()); - Assert.True(await service.RemoveMemberAsync(LabDatabase.OwnerA, LabDatabase.MemberA)); + Assert.True(await service.RemoveMemberAsync(LabDatabase.OwnerA, LabDatabase.MemberA, TestContext.Current.CancellationToken)); await using var verify = db.CreateDbContext(); - Assert.False(await verify.SandboxMembers.AnyAsync(x => x.UserKey == LabDatabase.MemberA)); - Assert.True(await verify.SandboxMembers.AnyAsync(x => x.UserKey == LabDatabase.MemberB)); + Assert.False(await verify.SandboxMembers.AnyAsync(x => x.UserKey == LabDatabase.MemberA, cancellationToken: TestContext.Current.CancellationToken)); + Assert.True(await verify.SandboxMembers.AnyAsync(x => x.UserKey == LabDatabase.MemberB, cancellationToken: TestContext.Current.CancellationToken)); } [Fact] @@ -46,7 +46,7 @@ public async Task Duplicate_owner_creation_rolls_back_all_new_sandbox_state() await using var db = await postgres.CreateAsync(); await db.SeedAsync(); var before = await db.SnapshotAsync(); - await Assert.ThrowsAsync(() => new SandboxService(db).CreateAsync(LabDatabase.OwnerA, "Duplicate")); + await Assert.ThrowsAsync(() => new SandboxService(db).CreateAsync(LabDatabase.OwnerA, "Duplicate", TestContext.Current.CancellationToken)); Assert.Equal(before, await db.SnapshotAsync()); } @@ -57,9 +57,9 @@ public async Task Concurrent_member_additions_cannot_exceed_capacity() await db.SeedAsync(); await using (var setup = db.CreateDbContext()) { - var settings = await setup.SandboxSettings.SingleAsync(x => x.Sandbox.OwnerUserKey == LabDatabase.OwnerA); + var settings = await setup.SandboxSettings.SingleAsync(x => x.Sandbox.OwnerUserKey == LabDatabase.OwnerA, cancellationToken: TestContext.Current.CancellationToken); settings.MaxUsers = 3; - await setup.SaveChangesAsync(); + await setup.SaveChangesAsync(TestContext.Current.CancellationToken); } var service = new SandboxService(db); async Task Add(string key) => await Record.ExceptionAsync(async () => @@ -68,8 +68,8 @@ public async Task Concurrent_member_additions_cannot_exceed_capacity() var outcomes = await Task.WhenAll(Add("candidate-1"), Add("candidate-2")); Assert.Single(outcomes, x => x is null); await using var verify = db.CreateDbContext(); - Assert.Equal(3, await verify.SandboxMembers.CountAsync(x => x.Sandbox.OwnerUserKey == LabDatabase.OwnerA)); - Assert.Equal(2, await verify.SandboxMembers.CountAsync(x => x.Sandbox.OwnerUserKey == LabDatabase.OwnerB)); + Assert.Equal(3, await verify.SandboxMembers.CountAsync(x => x.Sandbox.OwnerUserKey == LabDatabase.OwnerA, cancellationToken: TestContext.Current.CancellationToken)); + Assert.Equal(2, await verify.SandboxMembers.CountAsync(x => x.Sandbox.OwnerUserKey == LabDatabase.OwnerB, cancellationToken: TestContext.Current.CancellationToken)); } [Fact] @@ -78,7 +78,7 @@ public async Task Member_cannot_be_added_to_second_sandbox() await using var db = await postgres.CreateAsync(); await db.SeedAsync(); var before = await db.SnapshotAsync(); - await Assert.ThrowsAsync(() => new SandboxService(db).AddMemberAsync(LabDatabase.OwnerA, LabDatabase.MemberB)); + await Assert.ThrowsAsync(() => new SandboxService(db).AddMemberAsync(LabDatabase.OwnerA, LabDatabase.MemberB, TestContext.Current.CancellationToken)); Assert.Equal(before, await db.SnapshotAsync()); } [Fact] @@ -91,9 +91,9 @@ public async Task Concurrent_creation_cannot_create_two_sandboxes_for_one_owner( var outcomes = await Task.WhenAll(Create("First"), Create("Second")); Assert.Single(outcomes, x => x is null); await using var verify = db.CreateDbContext(); - Assert.Equal(1, await verify.Sandboxes.CountAsync()); - Assert.Equal(1, await verify.SandboxMembers.CountAsync()); - Assert.Equal(1, await verify.SandboxSettings.CountAsync()); + Assert.Equal(1, await verify.Sandboxes.CountAsync(cancellationToken: TestContext.Current.CancellationToken)); + Assert.Equal(1, await verify.SandboxMembers.CountAsync(cancellationToken: TestContext.Current.CancellationToken)); + Assert.Equal(1, await verify.SandboxSettings.CountAsync(cancellationToken: TestContext.Current.CancellationToken)); } [Theory] @@ -104,32 +104,32 @@ public async Task Ensure_restores_owner_relationship_without_changing_existing_s await using var db = await postgres.CreateAsync(); await db.SeedAsync(); var service = new SandboxService(db); - var original = (await service.GetByUserAsync(LabDatabase.OwnerA))!; + var original = (await service.GetByUserAsync(LabDatabase.OwnerA, TestContext.Current.CancellationToken))!; await using (var setup = db.CreateDbContext()) { - var owner = await setup.SandboxMembers.SingleAsync(x => x.UserKey == LabDatabase.OwnerA); + var owner = await setup.SandboxMembers.SingleAsync(x => x.UserKey == LabDatabase.OwnerA, cancellationToken: TestContext.Current.CancellationToken); if (missingMembership) setup.SandboxMembers.Remove(owner); else owner.Role = SandboxMemberRole.Member; - await setup.SaveChangesAsync(); + await setup.SaveChangesAsync(TestContext.Current.CancellationToken); } - var repaired = await service.EnsureAsync(LabDatabase.OwnerA, "Must not replace existing name"); + var repaired = await service.EnsureAsync(LabDatabase.OwnerA, "Must not replace existing name", TestContext.Current.CancellationToken); Assert.Equal(original.Id, repaired.Id); Assert.Equal(original.Name, repaired.Name); Assert.Equal(original.Settings.MaxUsers, repaired.Settings.MaxUsers); var beforeRepeat = await db.SnapshotAsync(); - await service.EnsureAsync(LabDatabase.OwnerA, "Still must not rename"); + await service.EnsureAsync(LabDatabase.OwnerA, "Still must not rename", TestContext.Current.CancellationToken); Assert.Equal(beforeRepeat, await db.SnapshotAsync()); await using var verify = db.CreateDbContext(); - var members = await verify.SandboxMembers.ToListAsync(); + var members = await verify.SandboxMembers.ToListAsync(cancellationToken: TestContext.Current.CancellationToken); Assert.Equal(4, members.Count); Assert.Equal(SandboxMemberRole.Owner, Assert.Single(members, x => x.UserKey == LabDatabase.OwnerA).Role); Assert.Contains(members, x => x.UserKey == LabDatabase.MemberA && x.SandboxId == original.Id); Assert.Contains(members, x => x.UserKey == LabDatabase.OwnerB && x.Role == SandboxMemberRole.Owner); Assert.Contains(members, x => x.UserKey == LabDatabase.MemberB && x.SandboxId != original.Id); - Assert.Equal(2, await verify.Sandboxes.CountAsync()); - Assert.Equal(2, await verify.SandboxSettings.CountAsync()); + Assert.Equal(2, await verify.Sandboxes.CountAsync(cancellationToken: TestContext.Current.CancellationToken)); + Assert.Equal(2, await verify.SandboxSettings.CountAsync(cancellationToken: TestContext.Current.CancellationToken)); } } diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Integration/UserWorkflowTests.cs b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Integration/UserWorkflowTests.cs index 7a26a678..79926be4 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Integration/UserWorkflowTests.cs +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Integration/UserWorkflowTests.cs @@ -53,15 +53,15 @@ public async Task Created_member_is_queryable_capacity_rejection_leaves_no_accou var sp = scope.ServiceProvider; var owner = await RegisterAsync(sp, db, "owner"); var foreignOwner = await RegisterAsync(sp, db, "foreign"); - var foreign = await Provisioning(sp, db, foreignOwner).CreateMemberAsync(foreignOwner, Request("foreign-member")); + var foreign = await Provisioning(sp, db, foreignOwner).CreateMemberAsync(foreignOwner, Request("foreign-member"), TestContext.Current.CancellationToken); Assert.True(foreign.IsSuccess, foreign.FailureReason); var foreignKey = UserKey.FromString(foreign.UserKey!); await using (var setup = db.CreateDbContext()) { - var settings = await setup.SandboxSettings.SingleAsync(x => x.Sandbox.OwnerUserKey == owner); + var settings = await setup.SandboxSettings.SingleAsync(x => x.Sandbox.OwnerUserKey == owner, cancellationToken: TestContext.Current.CancellationToken); settings.MaxUsers = 2; - await setup.SaveChangesAsync(); + await setup.SaveChangesAsync(TestContext.Current.CancellationToken); } var client = new Mock(MockBehavior.Strict); @@ -69,23 +69,23 @@ public async Task Created_member_is_queryable_capacity_rejection_leaves_no_accou client.SetupGet(x => x.Users).Returns(userClient.Object); var service = UserService(sp, db, owner, client.Object); - await service.CreateAsync(owner, Request("member")); - var visible = await service.GetUsersAsync(owner); + await service.CreateAsync(owner, Request("member"), TestContext.Current.CancellationToken); + var visible = await service.GetUsersAsync(owner, TestContext.Current.CancellationToken); Assert.NotNull(visible); var member = Assert.Single(visible.Users, x => !x.IsOwner).UserKey; Assert.Equal(2, visible.Users.Count); Assert.DoesNotContain(visible.Users, x => x.UserKey == foreignKey); var beforeRejected = await db.SnapshotAsync(); // The facade reports failures; assert persistence rather than dialog wording. - await service.CreateAsync(owner, Request("rejected-member")); + await service.CreateAsync(owner, Request("rejected-member"), TestContext.Current.CancellationToken); Assert.Equal(beforeRejected, await db.SnapshotAsync()); await using (var fresh = runtime.Services.CreateAsyncScope()) { var summaries = fresh.ServiceProvider.GetRequiredService().Create(TenantKeys.Single); - var all = await summaries.QueryAsync(new UserQuery { PageSize = 100, IncludeDeleted = true }); + var all = await summaries.QueryAsync(new UserQuery { PageSize = 100, IncludeDeleted = true }, TestContext.Current.CancellationToken); Assert.DoesNotContain(all.Items, x => x.UserName == "rejected-member"); } - Assert.False((await service.DeleteAsync(owner, foreignKey)).Success); + Assert.False((await service.DeleteAsync(owner, foreignKey, TestContext.Current.CancellationToken)).Success); Assert.Equal(beforeRejected, await db.SnapshotAsync()); userClient.VerifyNoOtherCalls(); var application = sp.GetRequiredService(); @@ -98,7 +98,7 @@ public async Task Created_member_is_queryable_capacity_rejection_leaves_no_accou return new UAuthResult { IsSuccess = true }; }); - Assert.True(await new SandboxAccountDeletionService(db, client.Object, NullLogger.Instance).DeleteMemberAsync(member)); + Assert.True(await new SandboxAccountDeletionService(db, client.Object, NullLogger.Instance).DeleteMemberAsync(member, TestContext.Current.CancellationToken)); userClient.Verify(x => x.DeleteMeAsync(), Times.Once); } else @@ -109,29 +109,29 @@ public async Task Created_member_is_queryable_capacity_rejection_leaves_no_accou var result = await application.DeleteUserAsync(Contexts.Create("users.delete.admin", owner, target), request); return new UAuthResult { IsSuccess = result.IsSuccess, Value = result }; }); - Assert.True((await service.DeleteAsync(owner, member)).Success); + Assert.True((await service.DeleteAsync(owner, member, TestContext.Current.CancellationToken)).Success); userClient.Verify(x => x.DeleteUserAsync(member, It.IsAny()), Times.Once); } await using var verification = runtime.Services.CreateAsyncScope(); var lifecycleStore = verification.ServiceProvider.GetRequiredService().Create(TenantKeys.Single); - var lifecycle = await lifecycleStore.GetAsync(new UserLifecycleKey(TenantKeys.Single, member)); + var lifecycle = await lifecycleStore.GetAsync(new UserLifecycleKey(TenantKeys.Single, member), TestContext.Current.CancellationToken); if (selfDelete) { Assert.NotNull(lifecycle); Assert.True(lifecycle.IsDeleted); } else Assert.Null(lifecycle); - Assert.NotNull(await lifecycleStore.GetAsync(new UserLifecycleKey(TenantKeys.Single, foreignKey))); - var remaining = await service.GetUsersAsync(owner); + Assert.NotNull(await lifecycleStore.GetAsync(new UserLifecycleKey(TenantKeys.Single, foreignKey), TestContext.Current.CancellationToken)); + var remaining = await service.GetUsersAsync(owner, TestContext.Current.CancellationToken); Assert.NotNull(remaining); Assert.Equal(owner, Assert.Single(remaining.Users).UserKey); // Optional dashboard calls may fail; its persisted count must still be usable. - var dashboard = await new LabDashboardService(new(db), client.Object).GetAsync(owner); + var dashboard = await new LabDashboardService(new(db), client.Object).GetAsync(owner, TestContext.Current.CancellationToken); Assert.NotNull(dashboard); Assert.Equal(1, dashboard.MemberCount); Assert.Equal(1, dashboard.RemainingCapacity); - await service.CreateAsync(owner, Request("replacement")); - Assert.Equal(2, (await service.GetUsersAsync(owner))!.Users.Count); + await service.CreateAsync(owner, Request("replacement"), TestContext.Current.CancellationToken); + Assert.Equal(2, (await service.GetUsersAsync(owner, TestContext.Current.CancellationToken))!.Users.Count); await using var final = db.CreateDbContext(); - Assert.False(await final.SandboxMembers.AnyAsync(x => x.UserKey == member)); - Assert.Equal(2, await final.SandboxMembers.CountAsync(x => x.Sandbox.OwnerUserKey == foreignOwner)); + Assert.False(await final.SandboxMembers.AnyAsync(x => x.UserKey == member, cancellationToken: TestContext.Current.CancellationToken)); + Assert.Equal(2, await final.SandboxMembers.CountAsync(x => x.Sandbox.OwnerUserKey == foreignOwner, cancellationToken: TestContext.Current.CancellationToken)); } [Theory] @@ -145,49 +145,49 @@ public async Task Owner_and_admin_recovery_restore_missing_membership_without_du var sp = scope.ServiceProvider; var owner = await RegisterAsync(sp, db, "repair-owner"); var other = await RegisterAsync(sp, db, "other-owner"); - var member = await Provisioning(sp, db, owner).CreateMemberAsync(owner, Request("retained-member")); + var member = await Provisioning(sp, db, owner).CreateMemberAsync(owner, Request("retained-member"), TestContext.Current.CancellationToken); Assert.True(member.IsSuccess, member.FailureReason); var roles = sp.GetRequiredService(); var assignments = sp.GetRequiredService(); await using (var setup = db.CreateDbContext()) { - setup.SandboxMembers.Remove(await setup.SandboxMembers.SingleAsync(x => x.UserKey == owner)); - await setup.SaveChangesAsync(); + setup.SandboxMembers.Remove(await setup.SandboxMembers.SingleAsync(x => x.UserKey == owner, cancellationToken: TestContext.Current.CancellationToken)); + await setup.SaveChangesAsync(TestContext.Current.CancellationToken); } if (adminRepair) { var context = Contexts.Create("identitylab.repair", LabAdminIdentity.UserKey); var service = new LabRepairService(db, sp.GetRequiredService(), roles, assignments, new LabAdminGuard(roles, assignments), NullLogger.Instance); - var snapshot = await service.GetSnapshotAsync(context); + var snapshot = await service.GetSnapshotAsync(context, TestContext.Current.CancellationToken); Assert.True(snapshot.UserScanComplete); Assert.Contains(snapshot.OrphanUsers, x => x.UserKey == owner && x.OwnedSandboxId != null); Assert.DoesNotContain(snapshot.OrphanUsers, x => x.UserKey == other); - Assert.True((await service.RepairOwnerAsync(context, owner)).Success); + Assert.True((await service.RepairOwnerAsync(context, owner, TestContext.Current.CancellationToken)).Success); var repaired = await db.SnapshotAsync(); - Assert.False((await service.RepairOwnerAsync(context, owner)).Success); + Assert.False((await service.RepairOwnerAsync(context, owner, TestContext.Current.CancellationToken)).Success); Assert.Equal(repaired, await db.SnapshotAsync()); - Assert.DoesNotContain((await service.GetSnapshotAsync(context)).OrphanUsers, x => x.UserKey == owner); + Assert.DoesNotContain((await service.GetSnapshotAsync(context, TestContext.Current.CancellationToken)).OrphanUsers, x => x.UserKey == owner); } else { var context = Contexts.Create("identitylab.repair", owner); var service = new LabOwnerRepairService(db, sp.GetRequiredService(), roles, assignments, NullLogger.Instance); - Assert.Equal(OwnerRepairState.CanRepair, (await service.GetStatusAsync(context)).State); - Assert.True((await service.RepairAsync(context)).Success); + Assert.Equal(OwnerRepairState.CanRepair, (await service.GetStatusAsync(context, TestContext.Current.CancellationToken)).State); + Assert.True((await service.RepairAsync(context, TestContext.Current.CancellationToken)).Success); var repaired = await db.SnapshotAsync(); - Assert.Equal(OwnerRepairState.NotRequired, (await service.GetStatusAsync(context)).State); - Assert.False((await service.RepairAsync(context)).Success); + Assert.Equal(OwnerRepairState.NotRequired, (await service.GetStatusAsync(context, TestContext.Current.CancellationToken)).State); + Assert.False((await service.RepairAsync(context, TestContext.Current.CancellationToken)).Success); Assert.Equal(repaired, await db.SnapshotAsync()); } await using var final = db.CreateDbContext(); - var members = await final.SandboxMembers.ToListAsync(); + var members = await final.SandboxMembers.ToListAsync(cancellationToken: TestContext.Current.CancellationToken); Assert.Equal(3, members.Count); var restored = Assert.Single(members, x => x.UserKey == owner); Assert.Equal(CodeBeam.UltimateAuth.IdentityLab.Domain.SandboxMemberRole.Owner, restored.Role); Assert.Contains(members, x => x.UserKey.Value == member.UserKey && x.SandboxId == restored.SandboxId); Assert.Contains(members, x => x.UserKey == other && x.SandboxId != restored.SandboxId); - Assert.Equal(2, await final.Sandboxes.CountAsync()); + Assert.Equal(2, await final.Sandboxes.CountAsync(cancellationToken: TestContext.Current.CancellationToken)); } } diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Provisioning/MemberProvisioningTests.cs b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Provisioning/MemberProvisioningTests.cs index c1a93792..3bb5675f 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Provisioning/MemberProvisioningTests.cs +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Provisioning/MemberProvisioningTests.cs @@ -31,7 +31,7 @@ public async Task Forged_actor_or_regular_member_cannot_provision(string request new(db), new(db), users.Object, roles.Object, assignments.Object, new(), NullLogger.Instance); await Assert.ThrowsAsync(() => service.CreateMemberAsync( - UserKey.FromString(requestedActor), new CreateUserRequest())); + UserKey.FromString(requestedActor), new CreateUserRequest(), TestContext.Current.CancellationToken)); Assert.Equal(before, await db.SnapshotAsync()); users.VerifyNoOtherCalls(); roles.VerifyNoOtherCalls(); @@ -83,14 +83,14 @@ public async Task Failed_role_assignment_rolls_back_only_created_user_and_token_ var service = new LabUserProvisioningService(ActorProvider.For(LabDatabase.OwnerA), new(db), new(db), users.Object, roles.Object, assignments.Object, gate, NullLogger.Instance); await Assert.ThrowsAsync(() => service.CreateMemberAsync( - LabDatabase.OwnerA, new CreateUserRequest())); + LabDatabase.OwnerA, new CreateUserRequest(), TestContext.Current.CancellationToken)); Assert.NotNull(rollbackContext); Assert.Equal(createdKey, rollbackContext!.TargetUserKey); Assert.Equal(DeleteMode.Hard, rollbackMode); Assert.True(unrelatedDenied); Assert.True(rollbackAllowed); Assert.True(replayDenied); - Assert.True((await policy.DecideAsync(rollbackContext!)).IsDenied); + Assert.True((await policy.DecideAsync(rollbackContext!, TestContext.Current.CancellationToken)).IsDenied); users.Verify(x => x.DeleteUserAsync(It.IsAny(), It.IsAny(), It.IsAny()), Times.Once); Assert.Equal(before, await db.SnapshotAsync()); } diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Provisioning/ProvisioningPolicyTests.cs b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Provisioning/ProvisioningPolicyTests.cs index ff1c440e..1911204f 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Provisioning/ProvisioningPolicyTests.cs +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Provisioning/ProvisioningPolicyTests.cs @@ -26,7 +26,7 @@ public async Task Client_values_cannot_forge_provisioning_or_rollback_tokens(obj UAuthActions.Users.DeleteAdmin }) { var result = await policy.DecideAsync(Contexts.Create(action, LabDatabase.OwnerA, - UserKey.FromString("unrelated"), attributes: attributes)); + UserKey.FromString("unrelated"), attributes: attributes), TestContext.Current.CancellationToken); Assert.True(result.IsDenied); } Assert.Equal(before, await db.SnapshotAsync()); diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Provisioning/RegistrationTests.cs b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Provisioning/RegistrationTests.cs index 38f108bf..507cbfab 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Provisioning/RegistrationTests.cs +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Provisioning/RegistrationTests.cs @@ -43,10 +43,10 @@ public async Task Registration_propagates_trusted_context_and_persists_matching_ assignments.Setup(x => x.Create(It.IsAny())).Returns(userStore.Object); var service = new LabRegistrationService(ActorProvider.For(null), users.Object, roles.Object, assignments.Object, new(db), new(db, NullLogger.Instance)); - var result = await service.RegisterAsync(new LabRegistrationRequest(" alice ", "alice@example.test", "test-password")); + var result = await service.RegisterAsync(new LabRegistrationRequest(" alice ", "alice@example.test", "test-password"), TestContext.Current.CancellationToken); Assert.True(result.IsSuccess); await using var verify = db.CreateDbContext(); - var sandbox = await verify.Sandboxes.Include(x => x.Members).SingleAsync(); + var sandbox = await verify.Sandboxes.Include(x => x.Members).SingleAsync(cancellationToken: TestContext.Current.CancellationToken); Assert.Equal(LabDatabase.OwnerA, sandbox.OwnerUserKey); Assert.Equal(LabDatabase.OwnerA, Assert.Single(sandbox.Members).UserKey); userStore.Verify(x => x.AssignAsync(LabDatabase.OwnerA, LabRoleIds.Owner, diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/README.md b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/README.md index 20985388..2b424ee9 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/README.md +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/README.md @@ -3,9 +3,11 @@ The security and integration tests require .NET 10 SDK and Docker with permission to start containers. Run from the repository root: ```sh -dotnet test tests/CodeBeam.UltimateAuth.IdentityLab.Tests -c Release --logger trx +dotnet test --project tests/CodeBeam.UltimateAuth.IdentityLab.Tests/CodeBeam.UltimateAuth.IdentityLab.Tests.csproj -c Release --report-trx ``` +The repository selects Microsoft.Testing.Platform in `global.json`. Use `--report-trx` for TRX output and `--filter-trait "Category=Render"` for trait filtering. CI collects coverage with `coverlet.MTP`; the Identity Lab job preserves the assembly include and migration-file exclusion through `--coverlet-include` and `--coverlet-exclude-by-file`. + By default, each database test class starts PostgreSQL 17 through Testcontainers. Every test gets a new database, applies the actual LabDbContext migrations, and drops its database on disposal. Security and integration tests never use EF InMemory. Nothing connects to application production databases. Alternatively set `IDENTITYLAB_TEST_POSTGRES` to a connection string for a **dedicated test PostgreSQL instance**. Its login needs CREATE DATABASE and permission to drop the databases it creates. Only databases named `identitylab_test_` are created/dropped. This setting bypasses Docker; it does not bypass relational tests. Missing infrastructure fails fixture initialization rather than silently skipping tests. @@ -14,14 +16,14 @@ Tests replace authenticated-context sources and selected framework store/client `Regression/SystemRoleIdentityTests.cs` records an expected rejection that the inspected implementation does not enforce. It is intentionally neither skipped nor inverted to accept unsafe behavior. The `KnownDefect=SystemRoleIdentity` trait identifies it; normal local and CI runs include it. Expect that regression to fail until separately approved production work addresses the issue. Do not enable this suite as a required green check before defect triage. -See [coverage matrix](docs/security-coverage.md) and [execution report](docs/execution-report.md). The project is included in the solution. Repository CI excludes it from the general matrix test step and runs it in a dedicated .NET 10 PostgreSQL job; the dedicated job uploads TRX results even on failure. +See [coverage matrix](docs/security-coverage.md) and [execution report](docs/execution-report.md). The project is included in the solution. Repository CI selects framework test projects separately and runs it in a dedicated .NET 10 PostgreSQL job; the dedicated job uploads TRX results even on failure. ## Page render suite The `Rendering` tests use bUnit and need only the .NET 10 SDK; no Docker or PostgreSQL is required: ```sh -dotnet test tests/CodeBeam.UltimateAuth.IdentityLab.Tests/CodeBeam.UltimateAuth.IdentityLab.Tests.csproj -c Release --filter Category=Render +dotnet test --project tests/CodeBeam.UltimateAuth.IdentityLab.Tests/CodeBeam.UltimateAuth.IdentityLab.Tests.csproj -c Release --filter-trait "Category=Render" ``` The real pages and child components render with MudBlazor services and a popover provider. External client calls use explicit Moq responses. Dashboard initialization uses real Lab services with a uniquely named EF InMemory LabDbContext store; this verifies component initialization and navigation branches, not relational behavior. Existing service regression tests and their PostgreSQL fixtures are unchanged. Direct page rendering does not exercise router authorization or layouts. diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Regression/SystemRoleIdentityTests.cs b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Regression/SystemRoleIdentityTests.cs index a6d147a6..c78ccaf4 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Regression/SystemRoleIdentityTests.cs +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Regression/SystemRoleIdentityTests.cs @@ -30,7 +30,7 @@ public async Task Owner_name_with_admin_id_must_be_rejected_before_assignment() assignments.Setup(x => x.Create(It.IsAny())).Returns(userStore.Object); var service = new LabRoleService(roles.Object, assignments.Object); await Assert.ThrowsAsync(() => service.AssignAsync( - TenantKeys.Single, LabDatabase.MemberA, "Owner")); + TenantKeys.Single, LabDatabase.MemberA, "Owner", TestContext.Current.CancellationToken)); userStore.Verify(x => x.AssignAsync(LabDatabase.MemberA, LabRoleIds.Admin, It.IsAny(), It.IsAny()), Times.Never); } diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Security/AdminGuardTests.cs b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Security/AdminGuardTests.cs index fd2fe01d..270db14e 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Security/AdminGuardTests.cs +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Security/AdminGuardTests.cs @@ -26,7 +26,7 @@ public async Task Admin_guard_uses_store_assignment_even_when_attributes_claim_l users.Setup(x => x.Create(It.IsAny())).Returns(userStore.Object); var context = Contexts.Create("identitylab.admin", LabDatabase.OwnerA, attributes: new Dictionary { ["roles"] = new[] { LabRoleIds.Admin } }); - Assert.Equal(assigned, await new LabAdminGuard(roles.Object, users.Object).IsAdminAsync(context)); + Assert.Equal(assigned, await new LabAdminGuard(roles.Object, users.Object).IsAdminAsync(context, TestContext.Current.CancellationToken)); } [Theory] @@ -39,7 +39,7 @@ public async Task Provisioning_role_helper_rejects_roles_outside_owner_and_user( var roles = new Mock(MockBehavior.Strict); var users = new Mock(MockBehavior.Strict); await Assert.ThrowsAsync(() => new CodeBeam.UltimateAuth.IdentityLab.Services.LabRoleService( - roles.Object, users.Object).AssignAsync(TenantKeys.Single, LabDatabase.MemberA, name)); + roles.Object, users.Object).AssignAsync(TenantKeys.Single, LabDatabase.MemberA, name, TestContext.Current.CancellationToken)); roles.VerifyNoOtherCalls(); users.VerifyNoOtherCalls(); } diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Security/RolePolicyTests.cs b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Security/RolePolicyTests.cs index 4bef7f72..865b7ca9 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Security/RolePolicyTests.cs +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Security/RolePolicyTests.cs @@ -15,7 +15,7 @@ public async Task Public_role_assignment_is_denied_even_when_context_claims_admi var context = Contexts.Create(UAuthActions.Authorization.Roles.AssignAdmin, CodeBeam.UltimateAuth.Core.Domain.UserKey.FromString(actor), LabDatabase.MemberA, attributes: new Dictionary { ["roles"] = new[] { LabRoleIds.Admin } }); - var result = await new LabApplicationPolicy().DecideAsync(context); + var result = await new LabApplicationPolicy().DecideAsync(context, TestContext.Current.CancellationToken); Assert.True(result.IsDenied); Assert.Equal("lab_role_management_restricted", result.DenyReason); } @@ -25,11 +25,11 @@ public async Task Trusted_system_role_assignment_and_authenticated_read_remain_a { var policy = new LabApplicationPolicy(); Assert.True((await policy.DecideAsync(Contexts.Create( - UAuthActions.Authorization.Roles.AssignAdmin, system: true))).IsAllowed); + UAuthActions.Authorization.Roles.AssignAdmin, system: true), TestContext.Current.CancellationToken)).IsAllowed); Assert.True((await policy.DecideAsync(Contexts.Create( - UAuthActions.Authorization.Roles.GetSelf, LabDatabase.MemberA))).IsAllowed); + UAuthActions.Authorization.Roles.GetSelf, LabDatabase.MemberA), TestContext.Current.CancellationToken)).IsAllowed); Assert.True((await policy.DecideAsync(Contexts.Create( - UAuthActions.Authorization.Roles.GetSelf))).IsDenied); + UAuthActions.Authorization.Roles.GetSelf), TestContext.Current.CancellationToken)).IsDenied); } [Theory] diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/AccountDeletionTests.cs b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/AccountDeletionTests.cs index dd0e29a3..0d3d99ee 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/AccountDeletionTests.cs +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/AccountDeletionTests.cs @@ -22,11 +22,11 @@ public async Task Membership_is_removed_only_after_framework_self_deletion_succe var client = new Mock(MockBehavior.Strict); client.SetupGet(x => x.Users).Returns(users.Object); var service = new SandboxAccountDeletionService(db, client.Object, NullLogger.Instance); - Assert.Equal(success, await service.DeleteMemberAsync(LabDatabase.MemberA)); + Assert.Equal(success, await service.DeleteMemberAsync(LabDatabase.MemberA, TestContext.Current.CancellationToken)); await using var verify = db.CreateDbContext(); - Assert.Equal(!success, await verify.SandboxMembers.AnyAsync(x => x.UserKey == LabDatabase.MemberA)); - Assert.True(await verify.SandboxMembers.AnyAsync(x => x.UserKey == LabDatabase.MemberB)); - Assert.True(await verify.SandboxMembers.AnyAsync(x => x.UserKey == LabDatabase.OwnerA)); + Assert.Equal(!success, await verify.SandboxMembers.AnyAsync(x => x.UserKey == LabDatabase.MemberA, cancellationToken: TestContext.Current.CancellationToken)); + Assert.True(await verify.SandboxMembers.AnyAsync(x => x.UserKey == LabDatabase.MemberB, cancellationToken: TestContext.Current.CancellationToken)); + Assert.True(await verify.SandboxMembers.AnyAsync(x => x.UserKey == LabDatabase.OwnerA, cancellationToken: TestContext.Current.CancellationToken)); } [Fact] @@ -37,7 +37,7 @@ public async Task Owner_self_deletion_never_calls_framework_client() var before = await db.SnapshotAsync(); var client = new Mock(MockBehavior.Strict); var service = new SandboxAccountDeletionService(db, client.Object, NullLogger.Instance); - Assert.False(await service.DeleteMemberAsync(LabDatabase.OwnerA)); + Assert.False(await service.DeleteMemberAsync(LabDatabase.OwnerA, TestContext.Current.CancellationToken)); client.VerifyNoOtherCalls(); Assert.Equal(before, await db.SnapshotAsync()); } diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/DashboardTests.cs b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/DashboardTests.cs index 5baaab3d..7a698f9b 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/DashboardTests.cs +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/DashboardTests.cs @@ -21,9 +21,9 @@ public async Task Overview_uses_own_membership_and_effective_capacity_when_optio await db.SeedAsync(); await using (var setup = db.CreateDbContext()) { - var settings = await setup.SandboxSettings.SingleAsync(x => x.Sandbox.OwnerUserKey == LabDatabase.OwnerA); + var settings = await setup.SandboxSettings.SingleAsync(x => x.Sandbox.OwnerUserKey == LabDatabase.OwnerA, cancellationToken: TestContext.Current.CancellationToken); settings.MaxUsers = configuredLimit; - await setup.SaveChangesAsync(); + await setup.SaveChangesAsync(TestContext.Current.CancellationToken); } var users = new Mock(MockBehavior.Strict); users.Setup(x => x.GetMeAsync(null)).ThrowsAsync(new InvalidOperationException("Profile unavailable")); @@ -39,7 +39,7 @@ public async Task Overview_uses_own_membership_and_effective_capacity_when_optio client.SetupGet(x => x.Authorization).Returns(authorization.Object); var service = new LabDashboardService(new(db), client.Object); - var owner = await service.GetAsync(LabDatabase.OwnerA); + var owner = await service.GetAsync(LabDatabase.OwnerA, TestContext.Current.CancellationToken); Assert.NotNull(owner); Assert.Equal("Sandbox A", owner.SandboxName); Assert.True(owner.IsOwner); @@ -53,14 +53,14 @@ public async Task Overview_uses_own_membership_and_effective_capacity_when_optio sessions.VerifyAll(); authorization.VerifyAll(); - var member = await service.GetAsync(LabDatabase.MemberA); + var member = await service.GetAsync(LabDatabase.MemberA, TestContext.Current.CancellationToken); Assert.NotNull(member); Assert.Equal(owner.SandboxId, member.SandboxId); Assert.False(member.IsOwner); Assert.Equal(owner.MemberCount, member.MemberCount); - Assert.True(await new SandboxService(db).RemoveMemberAsync(LabDatabase.OwnerA, LabDatabase.MemberA)); - var after = await service.GetAsync(LabDatabase.OwnerA); + Assert.True(await new SandboxService(db).RemoveMemberAsync(LabDatabase.OwnerA, LabDatabase.MemberA, TestContext.Current.CancellationToken)); + var after = await service.GetAsync(LabDatabase.OwnerA, TestContext.Current.CancellationToken); Assert.NotNull(after); Assert.Equal(1, after.MemberCount); Assert.Equal(expectedLimit - 1, after.RemainingCapacity); @@ -72,7 +72,7 @@ public async Task Missing_membership_returns_no_dashboard_without_requesting_opt await using var db = await postgres.CreateAsync(); await db.SeedAsync(); var client = new Mock(MockBehavior.Strict); - Assert.Null(await new LabDashboardService(new(db), client.Object).GetAsync(UserKey.FromString("orphan"))); + Assert.Null(await new LabDashboardService(new(db), client.Object).GetAsync(UserKey.FromString("orphan"), TestContext.Current.CancellationToken)); client.VerifyNoOtherCalls(); } } diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/DemoRoleServiceTests.cs b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/DemoRoleServiceTests.cs index 5077f1a9..bb5ec8fa 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/DemoRoleServiceTests.cs +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/DemoRoleServiceTests.cs @@ -26,7 +26,7 @@ public async Task Demo_operations_reject_system_and_forged_names_without_accessi var roles = new Mock(MockBehavior.Strict); var assignments = new Mock(MockBehavior.Strict); var service = new LabDemoRoleService(ActorProvider.For(LabDatabase.OwnerA), db, roles.Object, assignments.Object); - await Assert.ThrowsAsync(() => service.SetRoleAsync(LabDatabase.MemberA, role, true)); + await Assert.ThrowsAsync(() => service.SetRoleAsync(LabDatabase.MemberA, role, true, TestContext.Current.CancellationToken)); Assert.Equal(before, await db.SnapshotAsync()); roles.VerifyNoOtherCalls(); assignments.VerifyNoOtherCalls(); @@ -48,7 +48,7 @@ public async Task Assignment_requires_active_persisted_owner_and_same_sandbox_re var roles = new Mock(MockBehavior.Strict); var assignments = new Mock(MockBehavior.Strict); var service = new LabDemoRoleService(ActorProvider.For(UserKey.FromString(actor)), db, roles.Object, assignments.Object); - await Assert.ThrowsAsync(() => service.SetRoleAsync(UserKey.FromString(target), LabRoles.Manager, true)); + await Assert.ThrowsAsync(() => service.SetRoleAsync(UserKey.FromString(target), LabRoles.Manager, true, TestContext.Current.CancellationToken)); Assert.Equal(before, await db.SnapshotAsync()); roles.VerifyNoOtherCalls(); assignments.VerifyNoOtherCalls(); @@ -80,12 +80,12 @@ public async Task Demo_role_id_must_match_fixed_identity_before_any_assignment(b var service = new LabDemoRoleService(ActorProvider.For(LabDatabase.OwnerA), db, roles.Object, assignments.Object); if (forged) { - await Assert.ThrowsAsync(() => service.SetRoleAsync(LabDatabase.MemberA, "Manager", true)); + await Assert.ThrowsAsync(() => service.SetRoleAsync(LabDatabase.MemberA, "Manager", true, TestContext.Current.CancellationToken)); userStore.VerifyNoOtherCalls(); } else { - await service.SetRoleAsync(LabDatabase.MemberA, "Manager", true); + await service.SetRoleAsync(LabDatabase.MemberA, "Manager", true, TestContext.Current.CancellationToken); userStore.Verify(x => x.AssignAsync(LabDatabase.MemberA, LabRoleIds.Manager, It.IsAny(), It.IsAny()), Times.Once); } diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/DemoStoreTests.cs b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/DemoStoreTests.cs index f3ada7e1..1800303b 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/DemoStoreTests.cs +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/DemoStoreTests.cs @@ -37,11 +37,11 @@ public async Task Demo_store_only_accepts_exact_demo_permissions_from_fixed_role var roles = new Mock(); roles.Setup(x => x.Create(It.IsAny())).Returns(store.Object); var service = new LabDemoStoreService(ActorProvider.For(LabDatabase.MemberA), db, roles.Object, users.Object); - var actual = await service.GetPermissionsAsync(); + var actual = await service.GetPermissionsAsync(TestContext.Current.CancellationToken); Assert.True(actual.SetEquals(expected)); - await service.RequireAsync(LabDemoPermissions.ProductsRead); - await Assert.ThrowsAsync(() => service.RequireAsync("users.delete.admin")); + await service.RequireAsync(LabDemoPermissions.ProductsRead, TestContext.Current.CancellationToken); + await Assert.ThrowsAsync(() => service.RequireAsync("users.delete.admin", TestContext.Current.CancellationToken)); await db.SetStatusAsync(SandboxStatus.Suspended); - Assert.Empty(await service.GetPermissionsAsync()); + Assert.Empty(await service.GetPermissionsAsync(TestContext.Current.CancellationToken)); } } diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/RepairTests.cs b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/RepairTests.cs index fcf6fb76..df6481e0 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/RepairTests.cs +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/RepairTests.cs @@ -20,11 +20,11 @@ public async Task Repair_removes_only_own_stale_members_and_preserves_both_owner var stores = new Mock(); stores.Setup(x => x.Create(It.IsAny())).Returns(store.Object); var service = new SandboxRepairService(db, stores.Object, NullLogger.Instance); - await Assert.ThrowsAsync(() => service.RepairAsync(LabDatabase.MemberA)); - var result = await service.RepairAsync(LabDatabase.OwnerA); + await Assert.ThrowsAsync(() => service.RepairAsync(LabDatabase.MemberA, TestContext.Current.CancellationToken)); + var result = await service.RepairAsync(LabDatabase.OwnerA, TestContext.Current.CancellationToken); Assert.Equal(1, result.RemovedMemberships); await using var verify = db.CreateDbContext(); - var keys = await verify.SandboxMembers.Select(x => x.UserKey).ToListAsync(); + var keys = await verify.SandboxMembers.Select(x => x.UserKey).ToListAsync(cancellationToken: TestContext.Current.CancellationToken); Assert.DoesNotContain(LabDatabase.MemberA, keys); Assert.Contains(LabDatabase.MemberB, keys); Assert.Contains(LabDatabase.OwnerA, keys); @@ -36,7 +36,7 @@ public async Task Preview_matches_repair_and_repeated_repair_preserves_valid_and await using var db = await postgres.CreateAsync(); await db.SeedAsync(); var validKey = CodeBeam.UltimateAuth.Core.Domain.UserKey.FromString("valid-member"); - await new SandboxService(db).AddMemberAsync(LabDatabase.OwnerA, validKey); + await new SandboxService(db).AddMemberAsync(LabDatabase.OwnerA, validKey, TestContext.Current.CancellationToken); var store = new Mock(MockBehavior.Strict); store.Setup(x => x.GetByKeysAsync( It.IsAny>(), It.IsAny())) @@ -45,15 +45,15 @@ public async Task Preview_matches_repair_and_repeated_repair_preserves_valid_and stores.Setup(x => x.Create(It.IsAny())).Returns(store.Object); var service = new SandboxRepairService(db, stores.Object, NullLogger.Instance); var before = await db.SnapshotAsync(); - Assert.Equal(new SandboxRepairPreview(3, 1), await service.PreviewAsync(LabDatabase.OwnerA)); + Assert.Equal(new SandboxRepairPreview(3, 1), await service.PreviewAsync(LabDatabase.OwnerA, TestContext.Current.CancellationToken)); Assert.Equal(before, await db.SnapshotAsync()); - Assert.Equal(1, (await service.RepairAsync(LabDatabase.OwnerA)).RemovedMemberships); + Assert.Equal(1, (await service.RepairAsync(LabDatabase.OwnerA, TestContext.Current.CancellationToken)).RemovedMemberships); var repaired = await db.SnapshotAsync(); - Assert.Equal(new SandboxRepairPreview(2, 0), await service.PreviewAsync(LabDatabase.OwnerA)); - Assert.Equal(0, (await service.RepairAsync(LabDatabase.OwnerA)).RemovedMemberships); + Assert.Equal(new SandboxRepairPreview(2, 0), await service.PreviewAsync(LabDatabase.OwnerA, TestContext.Current.CancellationToken)); + Assert.Equal(0, (await service.RepairAsync(LabDatabase.OwnerA, TestContext.Current.CancellationToken)).RemovedMemberships); Assert.Equal(repaired, await db.SnapshotAsync()); await using var verify = db.CreateDbContext(); - var keys = await verify.SandboxMembers.Select(x => x.UserKey).ToListAsync(); + var keys = await verify.SandboxMembers.Select(x => x.UserKey).ToListAsync(cancellationToken: TestContext.Current.CancellationToken); Assert.Equal(4, keys.Count); Assert.Contains(validKey, keys); Assert.Contains(LabDatabase.OwnerA, keys); diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/SandboxAdminTests.cs b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/SandboxAdminTests.cs index 9ecc085e..e631298c 100644 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/SandboxAdminTests.cs +++ b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/Services/SandboxAdminTests.cs @@ -26,20 +26,20 @@ public async Task Capacity_changes_respect_existing_members_and_control_subseque await using var db = await postgres.CreateAsync(); await db.SeedAsync(); var sandboxes = new SandboxService(db); - var sandbox = (await sandboxes.GetByUserAsync(LabDatabase.OwnerA))!; + var sandbox = (await sandboxes.GetByUserAsync(LabDatabase.OwnerA, TestContext.Current.CancellationToken))!; var service = Create(db); var before = await db.SnapshotAsync(); - await Assert.ThrowsAsync(() => service.SetMaxUsersAsync(Admin, sandbox.Id, 1)); + await Assert.ThrowsAsync(() => service.SetMaxUsersAsync(Admin, sandbox.Id, 1, TestContext.Current.CancellationToken)); Assert.Equal(before, await db.SnapshotAsync()); - Assert.True(await service.SetMaxUsersAsync(Admin, sandbox.Id, 2)); - await Assert.ThrowsAsync(() => sandboxes.AddMemberAsync(LabDatabase.OwnerA, UserKey.FromString("new-member"))); - Assert.True(await service.SetMaxUsersAsync(Admin, sandbox.Id, 3)); - await sandboxes.AddMemberAsync(LabDatabase.OwnerA, UserKey.FromString("new-member")); - var summary = await service.GetAsync(Admin, sandbox.Id); + Assert.True(await service.SetMaxUsersAsync(Admin, sandbox.Id, 2, TestContext.Current.CancellationToken)); + await Assert.ThrowsAsync(() => sandboxes.AddMemberAsync(LabDatabase.OwnerA, UserKey.FromString("new-member"), TestContext.Current.CancellationToken)); + Assert.True(await service.SetMaxUsersAsync(Admin, sandbox.Id, 3, TestContext.Current.CancellationToken)); + await sandboxes.AddMemberAsync(LabDatabase.OwnerA, UserKey.FromString("new-member"), TestContext.Current.CancellationToken); + var summary = await service.GetAsync(Admin, sandbox.Id, TestContext.Current.CancellationToken); Assert.NotNull(summary); Assert.Equal(3, summary.MemberCount); Assert.Equal(3, summary.MaxUsers); - Assert.Equal(2, (await sandboxes.GetMembersAsync((await sandboxes.GetByUserAsync(LabDatabase.OwnerB))!.Id)).Count); + Assert.Equal(2, (await sandboxes.GetMembersAsync((await sandboxes.GetByUserAsync(LabDatabase.OwnerB, TestContext.Current.CancellationToken))!.Id, TestContext.Current.CancellationToken)).Count); } [Theory] @@ -50,10 +50,10 @@ public async Task Search_treats_postgres_pattern_characters_literally(string lit { await using var db = await postgres.CreateAsync(); await db.SeedAsync(); - var sandbox = (await new SandboxService(db).GetByUserAsync(LabDatabase.OwnerA))!; + var sandbox = (await new SandboxService(db).GetByUserAsync(LabDatabase.OwnerA, TestContext.Current.CancellationToken))!; var service = Create(db); - Assert.True(await service.RenameAsync(Admin, sandbox.Id, $" Lab {literal} special ")); - var result = await service.QueryAsync(Admin, new SandboxQuery { Search = $" {literal} " }); + Assert.True(await service.RenameAsync(Admin, sandbox.Id, $" Lab {literal} special ", TestContext.Current.CancellationToken)); + var result = await service.QueryAsync(Admin, new SandboxQuery { Search = $" {literal} " }, TestContext.Current.CancellationToken); Assert.Equal(1, result.TotalCount); Assert.Equal(sandbox.Id, Assert.Single(result.Items).Id); Assert.Equal($"Lab {literal} special", result.Items[0].Name); @@ -65,15 +65,15 @@ public async Task Statistics_follow_status_transitions_and_membership_removal() await using var db = await postgres.CreateAsync(); await db.SeedAsync(); var sandboxes = new SandboxService(db); - var a = (await sandboxes.GetByUserAsync(LabDatabase.OwnerA))!; - var b = (await sandboxes.GetByUserAsync(LabDatabase.OwnerB))!; + var a = (await sandboxes.GetByUserAsync(LabDatabase.OwnerA, TestContext.Current.CancellationToken))!; + var b = (await sandboxes.GetByUserAsync(LabDatabase.OwnerB, TestContext.Current.CancellationToken))!; var service = Create(db); - Assert.Equal(new SandboxStatistics(2, 2, 0, 0, 4), await service.GetStatisticsAsync(Admin)); - Assert.True(await sandboxes.RemoveMemberAsync(LabDatabase.OwnerA, LabDatabase.MemberA)); - Assert.True(await service.SetStatusAsync(Admin, a.Id, SandboxStatus.Passive)); - Assert.True(await service.SetStatusAsync(Admin, b.Id, SandboxStatus.Suspended)); - Assert.Equal(new SandboxStatistics(2, 0, 1, 1, 3), await service.GetStatisticsAsync(Admin)); - var suspended = await service.QueryAsync(Admin, new SandboxQuery { Status = SandboxStatus.Suspended }); + Assert.Equal(new SandboxStatistics(2, 2, 0, 0, 4), await service.GetStatisticsAsync(Admin, TestContext.Current.CancellationToken)); + Assert.True(await sandboxes.RemoveMemberAsync(LabDatabase.OwnerA, LabDatabase.MemberA, TestContext.Current.CancellationToken)); + Assert.True(await service.SetStatusAsync(Admin, a.Id, SandboxStatus.Passive, TestContext.Current.CancellationToken)); + Assert.True(await service.SetStatusAsync(Admin, b.Id, SandboxStatus.Suspended, TestContext.Current.CancellationToken)); + Assert.Equal(new SandboxStatistics(2, 0, 1, 1, 3), await service.GetStatisticsAsync(Admin, TestContext.Current.CancellationToken)); + var suspended = await service.QueryAsync(Admin, new SandboxQuery { Status = SandboxStatus.Suspended }, TestContext.Current.CancellationToken); Assert.Equal(1, suspended.TotalCount); Assert.Equal(b.Id, Assert.Single(suspended.Items).Id); } diff --git a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/identity-lab.runsettings b/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/identity-lab.runsettings deleted file mode 100644 index 87dfcfab..00000000 --- a/tests/CodeBeam.UltimateAuth.IdentityLab.Tests/identity-lab.runsettings +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - [CodeBeam.UltimateAuth.IdentityLab]* - **/Migrations/**/*.cs - cobertura,json - - - - - diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration.EfCore/CodeBeam.UltimateAuth.Tests.Integration.EfCore.csproj b/tests/CodeBeam.UltimateAuth.Tests.Integration.EfCore/CodeBeam.UltimateAuth.Tests.Integration.EfCore.csproj index ed85da6c..dc1e7447 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration.EfCore/CodeBeam.UltimateAuth.Tests.Integration.EfCore.csproj +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration.EfCore/CodeBeam.UltimateAuth.Tests.Integration.EfCore.csproj @@ -1,20 +1,16 @@  - net10.0 enable enable false - - - - - - + + + diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration.EfCore/UserCreationAtomicityTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration.EfCore/UserCreationAtomicityTests.cs index 61190150..36553b50 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration.EfCore/UserCreationAtomicityTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration.EfCore/UserCreationAtomicityTests.cs @@ -56,7 +56,7 @@ await EfCoreTestRuntime.CreateAsync( }; var exception = - await Assert.ThrowsAsync(() => service.CreateUserAsync(context, request)); + await Assert.ThrowsAsync(() => service.CreateUserAsync(context, request, TestContext.Current.CancellationToken)); exception.Message.Should().Be("simulated_identifier_store_failure"); @@ -96,17 +96,17 @@ await lifecycleFactory .GetAsync( new UserLifecycleKey( tenant, - userKey)); + userKey), TestContext.Current.CancellationToken); var profiles = await profileFactory .Create(tenant) - .GetAllProfilesByUserAsync(userKey); + .GetAllProfilesByUserAsync(userKey, TestContext.Current.CancellationToken); var identifiers = await identifierFactory .Create(tenant) - .GetByUserAsync(userKey); + .GetByUserAsync(userKey, TestContext.Current.CancellationToken); // // Atomicity contract: diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration.EfCore/UserQueryPaginationTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration.EfCore/UserQueryPaginationTests.cs index 67b0454c..6e75fc0c 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration.EfCore/UserQueryPaginationTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration.EfCore/UserQueryPaginationTests.cs @@ -54,7 +54,7 @@ public async Task QueryUsersAsync_AppliesConfiguredPaginationAndSorting() { UserName = $"{prefix}-{name}", DisplayName = $"{prefix}-{name}" - }); + }, TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); } @@ -81,7 +81,7 @@ public async Task QueryUsersAsync_AppliesConfiguredPaginationAndSorting() PageSize = 0, Search = prefix, SortBy = nameof(UserSummary.DisplayName) - }); + }, TestContext.Current.CancellationToken); firstPage.TotalCount.Should().Be(5); firstPage.PageNumber.Should().Be(1); @@ -106,7 +106,7 @@ public async Task QueryUsersAsync_AppliesConfiguredPaginationAndSorting() PageSize = 100, Search = prefix, SortBy = nameof(UserSummary.DisplayName) - }); + }, TestContext.Current.CancellationToken); secondPage.TotalCount.Should().Be(5); secondPage.PageNumber.Should().Be(2); @@ -163,7 +163,7 @@ public async Task QueryUsersAsync_AcrossPages_ReturnsEveryUserExactlyOnce() { UserName = $"{prefix}-{name}", DisplayName = $"{prefix}-{name}" - }); + }, TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); } @@ -191,7 +191,7 @@ public async Task QueryUsersAsync_AcrossPages_ReturnsEveryUserExactlyOnce() PageSize = 2, Search = prefix, SortBy = nameof(UserSummary.DisplayName) - }); + }, TestContext.Current.CancellationToken); pages.Add(result); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/AssemblyBehavior.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/AssemblyBehavior.cs index c7fc3b1f..0378b5a6 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/AssemblyBehavior.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/AssemblyBehavior.cs @@ -1 +1,3 @@ -[assembly: CollectionBehavior(DisableTestParallelization = true)] +using Xunit.v3; + +[assembly: Parallelization(Mode = Xunit.Sdk.ParallelMode.None)] diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/AuthServerTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/AuthServerTests.cs index 46245237..419a181e 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/AuthServerTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/AuthServerTests.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Mvc.Testing; -using Microsoft.VisualStudio.TestPlatform.TestHost; namespace CodeBeam.UltimateAuth.Tests.Integration; diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/AuthorizationManagementTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/AuthorizationManagementTests.cs index a4171484..73de9bf3 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/AuthorizationManagementTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/AuthorizationManagementTests.cs @@ -29,9 +29,9 @@ public async Task AssignRole_TargetMismatch_ShouldRejectWithoutMutation() { using var factory = NewFactory(); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); - var other = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var other = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await GrantAsync(factory, admin, UAuthActions.Authorization.Roles.AssignAdmin); @@ -61,9 +61,9 @@ public async Task RemoveRole_TargetMismatch_ShouldRejectWithoutMutation() { using var factory = NewFactory(); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); - var other = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var other = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await GrantAsync(factory, admin, UAuthActions.Authorization.Roles.RemoveAdmin); @@ -81,7 +81,7 @@ await GrantAsync(factory, admin, { UserKey = other.UserKey, RoleName = role.Name - }); + }, cancellationToken: TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -97,7 +97,7 @@ public async Task RenameRole_TargetMismatch_ShouldRejectBothTargets() { using var factory = NewFactory(); - var admin = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await GrantAsync(factory, admin, UAuthActions.Authorization.Roles.RenameAdmin); @@ -113,7 +113,7 @@ await GrantAsync(factory, admin, { Id = other.Id, Name = "unexpected-name" - }); + }, cancellationToken: TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -129,7 +129,7 @@ public async Task SetPermissions_TargetMismatch_ShouldNotModifyEitherRole() { using var factory = NewFactory(); - var admin = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await GrantAsync(factory, admin, UAuthActions.Authorization.Roles.SetPermissionsAdmin); @@ -145,7 +145,7 @@ await GrantAsync(factory, admin, { RoleId = other.Id, Permissions = [Permission.From("test.resource.write")] - }); + }, cancellationToken: TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -161,7 +161,7 @@ public async Task DeleteRole_TargetMismatch_ShouldPreserveBothRoles() { using var factory = NewFactory(); - var admin = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await GrantAsync(factory, admin, UAuthActions.Authorization.Roles.DeleteAdmin); @@ -176,7 +176,7 @@ await GrantAsync(factory, admin, new DeleteRoleRequest { Id = other.Id - }); + }, cancellationToken: TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -196,7 +196,7 @@ public async Task CreateRole_ValidRequest_ShouldPersistRole() { using var factory = NewFactory(); - var admin = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await GrantAsync(factory, admin, UAuthActions.Authorization.Roles.CreateAdmin); @@ -228,7 +228,7 @@ public async Task RenameRole_ValidRequest_ShouldPersistNewName() { using var factory = NewFactory(); - var admin = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await GrantAsync(factory, admin, UAuthActions.Authorization.Roles.RenameAdmin); @@ -262,7 +262,7 @@ public async Task SetPermissions_ValidRequest_ShouldReplacePermissions() { using var factory = NewFactory(); - var admin = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await GrantAsync( factory, @@ -309,8 +309,8 @@ public async Task AssignRole_ValidRequest_ShouldPersistAssignment() { using var factory = NewFactory(); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await GrantAsync(factory, admin, UAuthActions.Authorization.Roles.AssignAdmin); @@ -338,8 +338,8 @@ public async Task RemoveRole_ValidRequest_ShouldRemoveAssignment() { using var factory = NewFactory(); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await GrantAsync(factory, admin, UAuthActions.Authorization.Roles.RemoveAdmin); @@ -370,8 +370,8 @@ public async Task DeleteRole_ValidRequest_ShouldRemoveAssignments() { using var factory = NewFactory(); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await GrantAsync(factory, admin, UAuthActions.Authorization.Roles.DeleteAdmin); @@ -412,8 +412,8 @@ public async Task GetUserRoles_ShouldReturnAssignedRole() { using var factory = NewFactory(); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await GrantAsync(factory, admin, UAuthActions.Authorization.Roles.GetAdmin); @@ -426,12 +426,12 @@ await GrantAsync(factory, admin, using var response = await client.PostAsJsonAsync( UserRolesUrl(target.UserKey, "get"), - new RoleQuery()); + new RoleQuery(), cancellationToken: TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.OK); var result = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.UserKey.Should().Be(target.UserKey); @@ -444,7 +444,7 @@ public async Task GetMyRoles_ShouldReturnOwnRole() { using var factory = NewFactory(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var role = await SeedRoleAsync(factory); await SeedAssignmentAsync(factory, user.UserKey, role.Id); @@ -453,12 +453,12 @@ public async Task GetMyRoles_ShouldReturnOwnRole() using var response = await client.PostAsJsonAsync( $"{SelfBase}/roles/get", - new RoleQuery()); + new RoleQuery(), cancellationToken: TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.OK); var result = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.UserKey.Should().Be(user.UserKey); @@ -471,7 +471,7 @@ public async Task QueryRoles_ShouldReturnCreatedRole() { using var factory = NewFactory(); - var admin = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await GrantAsync( factory, @@ -484,12 +484,12 @@ await GrantAsync( using var response = await client.PostAsJsonAsync( $"{AdminBase}/roles/query", - new RoleQuery()); + new RoleQuery(), cancellationToken: TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.OK); var result = await response.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -506,7 +506,7 @@ public async Task AssignRole_Anonymous_ShouldReject() { using var factory = NewFactory(); - var target = await factory.CreateLoginUserAsync(); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var role = await SeedRoleAsync(factory); using var client = CreateClient(factory); @@ -530,8 +530,8 @@ public async Task AssignRole_WithoutPermission_ShouldNotMutate() { using var factory = NewFactory(); - var actor = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); + var actor = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var role = await SeedRoleAsync(factory); using var client = await AuthenticatedClientAsync(factory, actor); @@ -555,7 +555,7 @@ public async Task CreateRole_WithoutPermission_ShouldNotCreateRole() { using var factory = NewFactory(); - var actor = await factory.CreateLoginUserAsync(); + var actor = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var name = UniqueRoleName(); using var client = await AuthenticatedClientAsync(factory, actor); @@ -579,7 +579,7 @@ public async Task AuthorizationCheck_WithPermission_ShouldAllow() { using var factory = NewFactory(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await GrantAsync( factory, @@ -599,7 +599,7 @@ await GrantAsync( response.StatusCode.Should().Be(HttpStatusCode.OK); var result = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.IsAllowed.Should().BeTrue(); @@ -610,7 +610,7 @@ public async Task AuthorizationCheck_WithoutPermission_ShouldReturnDenial() { using var factory = NewFactory(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = await AuthenticatedClientAsync(factory, user); @@ -625,7 +625,7 @@ public async Task AuthorizationCheck_WithoutPermission_ShouldReturnDenial() response.StatusCode.Should().Be(HttpStatusCode.OK); var result = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.IsAllowed.Should().BeFalse(); @@ -637,9 +637,9 @@ public async Task AssignRole_ServiceTargetMismatch_ShouldReject() { using var factory = NewFactory(); - var admin = await factory.CreateLoginUserAsync(); - var authorizedTarget = await factory.CreateLoginUserAsync(); - var other = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var authorizedTarget = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var other = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await GrantAsync( factory, @@ -678,9 +678,9 @@ public async Task RemoveRole_ServiceTargetMismatch_ShouldReject() { using var factory = NewFactory(); - var admin = await factory.CreateLoginUserAsync(); - var authorizedTarget = await factory.CreateLoginUserAsync(); - var other = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var authorizedTarget = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var other = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await GrantAsync( factory, @@ -744,13 +744,13 @@ public async Task RoleStore_ShouldIsolateRolesBetweenTenants() permissions: [], now: factory.Clock.UtcNow); - await storeFactory.Create(tenantA).AddAsync(role); + await storeFactory.Create(tenantA).AddAsync(role, TestContext.Current.CancellationToken); var fromA = await storeFactory.Create(tenantA) - .GetAsync(new RoleKey(tenantA, role.Id)); + .GetAsync(new RoleKey(tenantA, role.Id), TestContext.Current.CancellationToken); var fromB = await storeFactory.Create(tenantB) - .GetAsync(new RoleKey(tenantB, role.Id)); + .GetAsync(new RoleKey(tenantB, role.Id), TestContext.Current.CancellationToken); fromA.Should().NotBeNull(); fromB.Should().BeNull(); @@ -761,7 +761,7 @@ public async Task GetMyPermissions_WhenRolePermissionsChange_ReturnsUpdatedPermi { using var factory = NewFactory(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var oldPermission = Permission.From("reports.read"); var newPermission = Permission.From("reports.export"); @@ -785,7 +785,7 @@ await SeedAssignmentAsync( response.StatusCode.Should().Be(HttpStatusCode.OK); var result = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -801,7 +801,7 @@ await SeedAssignmentAsync( var store = roleStoreFactory.Create(TenantKeys.Single); - var stored = await store.GetAsync(new RoleKey(TenantKeys.Single, role.Id)); + var stored = await store.GetAsync(new RoleKey(TenantKeys.Single, role.Id), TestContext.Current.CancellationToken); stored.Should().NotBeNull(); @@ -815,11 +815,11 @@ await SeedAssignmentAsync( stored.Permissions.Should().Contain(newPermission); stored.Permissions.Should().NotContain(oldPermission); - await store.SaveAsync(stored, expectedVersion); + await store.SaveAsync(stored, expectedVersion, TestContext.Current.CancellationToken); // Check persistence using a fresh read. var saved = await store.GetAsync( - new RoleKey(TenantKeys.Single, role.Id)); + new RoleKey(TenantKeys.Single, role.Id), TestContext.Current.CancellationToken); saved.Should().NotBeNull(); saved!.Permissions.Should().Contain(newPermission); @@ -843,7 +843,7 @@ await SeedAssignmentAsync( // 1. Was the role updated? var updatedRole = await roleStore.GetAsync( - new RoleKey(TenantKeys.Single, role.Id)); + new RoleKey(TenantKeys.Single, role.Id), TestContext.Current.CancellationToken); updatedRole.Should().NotBeNull(); @@ -852,14 +852,14 @@ await SeedAssignmentAsync( // 2. Does the user still have the role? var assignments = await userRoleStore.GetAssignmentsAsync( - user.UserKey); + user.UserKey, TestContext.Current.CancellationToken); assignments.Should().Contain(x => x.RoleId == role.Id); // 3. Can the permission store resolve the updated permission? var permissions = await permissionStore.GetPermissionsAsync( TenantKeys.Single, - user.UserKey); + user.UserKey, TestContext.Current.CancellationToken); permissions.Should().Contain(newPermission); permissions.Should().NotContain(oldPermission); @@ -868,12 +868,12 @@ await SeedAssignmentAsync( // Second request: the new permission must be visible. using (var response = await client.PostAsJsonAsync( $"{SelfBase}/permissions/get", - new { })) + new { }, cancellationToken: TestContext.Current.CancellationToken)) { response.StatusCode.Should().Be(HttpStatusCode.OK); var result = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -891,7 +891,7 @@ public async Task GetMyPermissions_Anonymous_ShouldReturnUnauthorized() using var response = await client.PostAsJsonAsync( $"{SelfBase}/permissions/get", - new { }); + new { }, cancellationToken: TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } @@ -901,7 +901,7 @@ public async Task GetMyPermissions_WhenNoRoles_ReturnsEmpty() { using var factory = NewFactory(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = await AuthenticatedClientAsync(factory, user); @@ -912,7 +912,7 @@ public async Task GetMyPermissions_WhenNoRoles_ReturnsEmpty() response.StatusCode.Should().Be(HttpStatusCode.OK); var result = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.Permissions.Should().BeEmpty(); @@ -923,8 +923,8 @@ public async Task GetMyPermissions_DoesNotReturnOtherUsersPermissions() { using var factory = NewFactory(); - var user = await factory.CreateLoginUserAsync(); - var other = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var other = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var permission = Permission.From("reports.export"); @@ -946,7 +946,7 @@ await SeedAssignmentAsync( response.StatusCode.Should().Be(HttpStatusCode.OK); var result = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/ClientRateLimitingIntegrationTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/ClientRateLimitingIntegrationTests.cs index f4f61084..5d6a180a 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/ClientRateLimitingIntegrationTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/ClientRateLimitingIntegrationTests.cs @@ -26,27 +26,27 @@ public async Task NativeValidation429PreservesClientState_AndLaterRevocationStil { using var factory = AuthServerFactory.Create(o => { o.RateLimiting.Enabled = true; o.RateLimiting.Endpoints[UAuthEndpoint.Validate] = new() { PermitLimit = 1 }; }); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var http = await Login(factory, user); var transport = new HttpTransport(http); using var services = ClientServices(factory, transport); using var scope = services.CreateScope(); var manager = scope.ServiceProvider.GetRequiredService(); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); Assert.True(manager.State.IsAuthenticated); var validatedAt = manager.State.LastValidatedAt; - await manager.EnsureAsync(force: true); - await manager.EnsureAsync(force: true); + await manager.EnsureAsync(force: true, ct: TestContext.Current.CancellationToken); + await manager.EnsureAsync(force: true, ct: TestContext.Current.CancellationToken); Assert.True(manager.State.IsAuthenticated); Assert.Equal(validatedAt, manager.State.LastValidatedAt); Assert.Equal(2, transport.Count); using (var serverScope = factory.Services.CreateScope()) { var store = serverScope.ServiceProvider.GetRequiredService().Create(TenantKey.Single); - await store.ExecuteAsync(ct => store.RevokeRootCascadeAsync(user.UserKey, factory.Clock.UtcNow, ct)); + await store.ExecuteAsync(ct => store.RevokeRootCascadeAsync(user.UserKey, factory.Clock.UtcNow, ct), TestContext.Current.CancellationToken); } factory.Clock.Advance(TimeSpan.FromMinutes(2)); - await manager.EnsureAsync(force: true); + await manager.EnsureAsync(force: true, ct: TestContext.Current.CancellationToken); Assert.False(manager.State.IsAuthenticated); } @@ -55,7 +55,7 @@ public async Task NativeRefresh429PreservesCredentials_AndSuppressesImmediateRef { using var factory = AuthServerFactory.Create(o => { o.RateLimiting.Enabled = true; o.RateLimiting.Endpoints[UAuthEndpoint.Refresh] = new() { PermitLimit = 1 }; }); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var http = await Login(factory, user); var credential = http.DefaultRequestHeaders.GetValues("Cookie").Single(); var transport = new HttpTransport(http); @@ -72,7 +72,7 @@ public async Task NativeRefresh429PreservesCredentials_AndSuppressesImmediateRef Assert.Equal(2, transport.Count); var session = await CsrfTestClient.PostAsync(http, "/auth/validate", null); Assert.Equal(HttpStatusCode.OK, session.StatusCode); - Assert.True((await session.Content.ReadFromJsonAsync())!.IsValid); + Assert.True((await session.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken))!.IsValid); } private static async Task Login(AuthServerFactory factory, IntegrationTestUser user) diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/CodeBeam.UltimateAuth.Tests.Integration.csproj b/tests/CodeBeam.UltimateAuth.Tests.Integration/CodeBeam.UltimateAuth.Tests.Integration.csproj index b876ff76..b6a5b7a8 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/CodeBeam.UltimateAuth.Tests.Integration.csproj +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/CodeBeam.UltimateAuth.Tests.Integration.csproj @@ -1,22 +1,18 @@  - net10.0 enable enable false - - - - - - - - + + + + + diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/CredentialManagementTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/CredentialManagementTests.cs index fbe24202..1075fc27 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/CredentialManagementTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/CredentialManagementTests.cs @@ -33,7 +33,7 @@ public async Task AnonymousReset_WithValidToken_ChangesPassword() factory.Clock.Reset(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient(factory); @@ -44,7 +44,7 @@ public async Task AnonymousReset_WithValidToken_ChangesPassword() Identifier = user.Identifier, CredentialType = CredentialType.Password, ResetCodeType = ResetCodeType.Token - }); + }, cancellationToken: TestContext.Current.CancellationToken); beginResponse.StatusCode.Should().Be(HttpStatusCode.OK); @@ -63,7 +63,7 @@ public async Task AnonymousReset_WithValidToken_ChangesPassword() CredentialType = CredentialType.Password, ResetToken = notification.Token, NewSecret = newPassword - }); + }, cancellationToken: TestContext.Current.CancellationToken); completeResponse.StatusCode.Should().Be(HttpStatusCode.OK); @@ -78,7 +78,7 @@ public async Task AnonymousReset_WithValidToken_ChangesPassword() var store = storeFactory.Create(notification.Tenant); - var credentials = await store.GetByUserAsync(user.UserKey); + var credentials = await store.GetByUserAsync(user.UserKey, TestContext.Current.CancellationToken); var password = credentials .OfType() @@ -119,13 +119,13 @@ public async Task AdminBeginReset_TargetMismatch_ShouldRejectWithoutNotification using var factory = CreateFactory(notifier); factory.Clock.Reset(); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); - var other = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var other = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await factory.GrantPermissionsAsync( admin.UserKey, - [UAuthActions.Credentials.BeginResetAdmin]); + [UAuthActions.Credentials.BeginResetAdmin], TestContext.Current.CancellationToken); using var client = CreateClient(factory); await AuthenticateAsync(client, admin); @@ -149,12 +149,12 @@ public async Task AdminBeginReset_ValidTarget_ShouldNotifyTarget() using var factory = CreateFactory(notifier); factory.Clock.Reset(); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await factory.GrantPermissionsAsync( admin.UserKey, - [UAuthActions.Credentials.BeginResetAdmin]); + [UAuthActions.Credentials.BeginResetAdmin], TestContext.Current.CancellationToken); using var client = CreateClient(factory); await AuthenticateAsync(client, admin); @@ -167,7 +167,7 @@ await factory.GrantPermissionsAsync( response.StatusCode.Should().Be(HttpStatusCode.OK); var result = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.ExpiresAt.Should().BeAfter(factory.Clock.UtcNow); @@ -175,7 +175,7 @@ await factory.GrantPermissionsAsync( notifier.Notifications.Should() .ContainSingle(x => x.UserKey == target.UserKey); - var body = await response.Content.ReadAsStringAsync(); + var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); body.Should().NotContain( notifier.Notifications.Single().Token); @@ -189,13 +189,13 @@ public async Task AdminCompleteReset_TargetMismatch_ShouldNotChangeOtherPassword using var factory = CreateFactory(notifier); factory.Clock.Reset(); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); - var other = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var other = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await factory.GrantPermissionsAsync( admin.UserKey, - [UAuthActions.Credentials.CompleteResetAdmin]); + [UAuthActions.Credentials.CompleteResetAdmin], TestContext.Current.CancellationToken); // B için gerçekten geçerli bir reset token'ı üret. using var anonymousClient = CreateClient(factory); @@ -234,15 +234,15 @@ public async Task AdminCompleteReset_ValidTarget_ShouldChangePassword() using var factory = CreateFactory(notifier); factory.Clock.Reset(); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Credentials.BeginResetAdmin, UAuthActions.Credentials.CompleteResetAdmin - ]); + ], TestContext.Current.CancellationToken); using var client = CreateClient(factory); await AuthenticateAsync(client, admin); @@ -257,7 +257,7 @@ await factory.GrantPermissionsAsync( begin.StatusCode.Should().Be(HttpStatusCode.OK); var beginResult = await begin.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); beginResult.Should().NotBeNull(); @@ -277,7 +277,7 @@ await factory.GrantPermissionsAsync( complete.StatusCode.Should().Be(HttpStatusCode.OK); var completeResult = await complete.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); completeResult.Should().NotBeNull(); completeResult!.IsSuccess.Should().BeTrue(); @@ -294,7 +294,7 @@ public async Task AnonymousCompleteReset_ReusedToken_ShouldReject() using var factory = CreateFactory(notifier); factory.Clock.Reset(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient(factory); @@ -340,8 +340,8 @@ public async Task AdminBeginReset_WithoutPermission_ShouldNotNotify() using var factory = CreateFactory(notifier); factory.Clock.Reset(); - var actor = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); + var actor = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient(factory); await AuthenticateAsync(client, actor); @@ -363,7 +363,7 @@ public async Task AdminBeginReset_Unauthenticated_ShouldReject() using var factory = CreateFactory(notifier); factory.Clock.Reset(); - var target = await factory.CreateLoginUserAsync(); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient(factory); @@ -385,7 +385,7 @@ public async Task AnonymousCompleteReset_InvalidProof_ShouldNotRevealIdentifierE using var factory = CreateFactory(notifier); factory.Clock.Reset(); - var existingUser = await factory.CreateLoginUserAsync(); + var existingUser = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient(factory); @@ -411,8 +411,8 @@ public async Task AnonymousCompleteReset_InvalidProof_ShouldNotRevealIdentifierE .Be(unknownResponse.StatusCode); // Same observable response body. - var existingBody = await existingResponse.Content.ReadAsStringAsync(); - var unknownBody = await unknownResponse.Content.ReadAsStringAsync(); + var existingBody = await existingResponse.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); + var unknownBody = await unknownResponse.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); existingBody.Should().Be(unknownBody); @@ -431,7 +431,7 @@ public async Task AnonymousRecovery_DefaultPolicy_RevokesAllExistingChains() using var factory = CreateFactory(notifier); factory.Clock.Reset(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); // Two independent clients with different X-UDID values. using var firstClient = CreateClient(factory); @@ -448,7 +448,7 @@ public async Task AnonymousRecovery_DefaultPolicy_RevokesAllExistingChains() var store = sessionFactory.Create(TenantKeys.Single); - var chains = await store.GetChainsByUserAsync(user.UserKey); + var chains = await store.GetChainsByUserAsync(user.UserKey, ct: TestContext.Current.CancellationToken); chains.Should().HaveCount(2); chains.Should().OnlyContain(c => !c.IsRevoked); @@ -491,7 +491,7 @@ public async Task AnonymousRecovery_DefaultPolicy_RevokesAllExistingChains() var store = sessionFactory.Create(TenantKeys.Single); - var chains = await store.GetChainsByUserAsync(user.UserKey); + var chains = await store.GetChainsByUserAsync(user.UserKey, ct: TestContext.Current.CancellationToken); chains.Should().HaveCount(2); chains.Should().OnlyContain(c => c.IsRevoked); @@ -518,7 +518,7 @@ public async Task Recovery_ShouldRespectConfiguredSessionPolicy(CredentialSessio factory.Clock.Reset(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var firstClient = CreateClient(factory); using var secondClient = CreateClient(factory); @@ -578,8 +578,8 @@ public async Task Recovery_RevokeAll_ShouldNotAffectOtherUsers() using var factory = CreateFactory(notifier); factory.Clock.Reset(); - var target = await factory.CreateLoginUserAsync(); - var other = await factory.CreateLoginUserAsync(); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var other = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var targetClient = CreateClient(factory); using var otherClient = CreateClient(factory); @@ -630,7 +630,7 @@ public async Task Recovery_InvalidToken_ShouldNotRevokeExistingChains() using var factory = CreateFactory(notifier); factory.Clock.Reset(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var authenticatedClient = CreateClient(factory); await AuthenticateAsync(authenticatedClient, user); @@ -675,7 +675,7 @@ public async Task SelfChange_ShouldRespectConfiguredSessionPolicy(CredentialSess factory.Clock.Reset(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var currentClient = CreateClient(factory); using var otherClient = CreateClient(factory); @@ -731,12 +731,12 @@ public async Task AdminChange_ShouldRespectConfiguredSessionPolicy(CredentialSes factory.Clock.Reset(); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await factory.GrantPermissionsAsync( admin.UserKey, - [UAuthActions.Credentials.ChangeAdmin]); + [UAuthActions.Credentials.ChangeAdmin], TestContext.Current.CancellationToken); using var adminClient = CreateClient(factory); using var targetClient1 = CreateClient(factory); @@ -782,7 +782,7 @@ public async Task AnonymousBeginReset_AlreadyActive_ShouldNotRevealResetState() using var factory = CreateFactory(notifier); factory.Clock.Reset(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient(factory); @@ -807,8 +807,8 @@ public async Task AnonymousBeginReset_AlreadyActive_ShouldNotRevealResetState() active.StatusCode.Should().Be(unknown.StatusCode); - (await active.Content.ReadAsStringAsync()) - .Should().Be(await unknown.Content.ReadAsStringAsync()); + (await active.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)) + .Should().Be(await unknown.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)); notifier.Notifications.Should().HaveCount(notificationCount); } @@ -821,7 +821,7 @@ public async Task AnonymousCompleteReset_ExpiredToken_ShouldNotRevealResetState( using var factory = CreateFactory(notifier); factory.Clock.Reset(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient(factory); @@ -854,8 +854,8 @@ public async Task AnonymousCompleteReset_ExpiredToken_ShouldNotRevealResetState( expired.StatusCode.Should().Be(unknown.StatusCode); - (await expired.Content.ReadAsStringAsync()) - .Should().Be(await unknown.Content.ReadAsStringAsync()); + (await expired.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)) + .Should().Be(await unknown.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)); await AssertPasswordAsync(factory, user, user.Secret); } @@ -868,7 +868,7 @@ public async Task AnonymousBeginReset_InvalidCodeType_ShouldRejectConsistently() using var factory = CreateFactory(notifier); factory.Clock.Reset(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient(factory); @@ -892,8 +892,8 @@ async Task SendAsync(string identifier) existing.StatusCode.Should().Be(HttpStatusCode.BadRequest); unknown.StatusCode.Should().Be(HttpStatusCode.BadRequest); - using var existingJson = JsonDocument.Parse(await existing.Content.ReadAsStringAsync()); - using var unknownJson = JsonDocument.Parse(await unknown.Content.ReadAsStringAsync()); + using var existingJson = JsonDocument.Parse(await existing.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)); + using var unknownJson = JsonDocument.Parse(await unknown.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)); existingJson.RootElement.GetProperty("status").GetInt32() .Should().Be(unknownJson.RootElement.GetProperty("status").GetInt32()); @@ -915,7 +915,7 @@ public async Task AnonymousCompleteReset_SamePassword_ShouldNotRevealCredentialS using var factory = CreateFactory(notifier); factory.Clock.Reset(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient(factory); @@ -944,8 +944,8 @@ public async Task AnonymousCompleteReset_SamePassword_ShouldNotRevealCredentialS samePassword.StatusCode.Should().Be(unknown.StatusCode); - (await samePassword.Content.ReadAsStringAsync()) - .Should().Be(await unknown.Content.ReadAsStringAsync()); + (await samePassword.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)) + .Should().Be(await unknown.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)); await AssertPasswordAsync(factory, user, user.Secret); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/HttpContextJsonContractTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/HttpContextJsonContractTests.cs index 22ea0305..47f833fb 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/HttpContextJsonContractTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/HttpContextJsonContractTests.cs @@ -65,7 +65,7 @@ public async Task InvalidJsonRequest_ShouldReturn400ProblemDetails( request.Content.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType); - using var response = await host.Client.SendAsync(request); + using var response = await host.Client.SendAsync(request, TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -73,7 +73,7 @@ public async Task InvalidJsonRequest_ShouldReturn400ProblemDetails( .MediaType.Should().Be("application/problem+json"); var problem = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); problem.Should().NotBeNull(); problem!.Status.Should().Be(400); @@ -89,12 +89,12 @@ public async Task ValidJsonRequest_ShouldReturn200WithTypedResult() using var response = await host.Client.PostAsJsonAsync( Endpoint, - new { name = "UltimateAuth" }); + new { name = "UltimateAuth" }, cancellationToken: TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.OK); var result = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.Name.Should().Be("UltimateAuth"); @@ -113,12 +113,12 @@ public async Task MissingContentType_ShouldReturn400ProblemDetails() Encoding.UTF8.GetBytes("""{"name":"test"}""")) }; - using var response = await host.Client.SendAsync(request); + using var response = await host.Client.SendAsync(request, TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); var problem = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); problem!.Detail.Should().Be("request_content_type_invalid"); } @@ -130,12 +130,12 @@ public async Task JsonReader_ShouldRewindBodyAfterSuccess() using var response = await host.Client.PostAsJsonAsync( "/test/json-rewind", - new { name = "UltimateAuth" }); + new { name = "UltimateAuth" }, cancellationToken: TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.OK); var result = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.Name.Should().Be("UltimateAuth"); @@ -161,7 +161,7 @@ public async Task InvalidPermission_ShouldReturn400ProblemDetails() using var response = await host.Client.PostAsync( "/test/json-permission", - content); + content, TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -169,7 +169,7 @@ public async Task InvalidPermission_ShouldReturn400ProblemDetails() .MediaType.Should().Be("application/problem+json"); var problem = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); problem.Should().NotBeNull(); problem!.Status.Should().Be(400); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/LoginCsrfExploitProofTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/LoginCsrfExploitProofTests.cs index dc7374bd..13f6b469 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/LoginCsrfExploitProofTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/LoginCsrfExploitProofTests.cs @@ -14,7 +14,7 @@ public async Task CrossOriginLogin_WithoutCsrfProof_MustNotAuthenticateVictimAsA { await using var factory = new AuthServerFactory(); - var attacker = await factory.CreateLoginUserAsync(); + var attacker = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"csrf-victim-{Guid.NewGuid():N}"; using var victim = CreateBrowser(factory, deviceId); @@ -38,9 +38,9 @@ public async Task CrossOriginLogin_WithoutCsrfProof_MustNotAuthenticateVictimAsA "Origin", "https://localhost:6130"); - using var response = await victim.SendAsync(request); + using var response = await victim.SendAsync(request, TestContext.Current.CancellationToken); - var body = await response.Content.ReadAsStringAsync(); + var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); // Prove that the rejection came from CSRF validation. response.StatusCode.Should().Be( diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/LoginTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/LoginTests.cs index 60dc1bfd..5cdc5fc4 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/LoginTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/LoginTests.cs @@ -9,23 +9,21 @@ namespace CodeBeam.UltimateAuth.Tests.Integration; -public class LoginTests : IClassFixture +public class LoginTests : IDisposable { private const string LoginEndpoint = "/auth/login"; private const string ValidIdentifier = "admin"; private const string ValidSecret = "admin"; - private readonly AuthServerFactory _factory; + // Each test owns its host so clock changes and account state cannot leak. + private readonly AuthServerFactory _factory = new(); - public LoginTests(AuthServerFactory factory) - { - _factory = factory; - } + public void Dispose() => _factory.Dispose(); [Fact] public async Task Login_WithValidCredentials_ShouldIssueSessionCredential() { - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient($"valid-session-{Guid.NewGuid():N}"); @@ -49,7 +47,7 @@ public async Task Login_WithValidCredentials_ShouldIssueSessionCredential() [Fact] public async Task Login_WithValidCredentials_ShouldCreateUsableAuthenticatedSession() { - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"usable-session-{Guid.NewGuid():N}"; using var client = CreateClient(deviceId); var loginResponse = await LoginAsync(client, user.Identifier, user.Secret); @@ -70,7 +68,7 @@ public async Task Login_WithValidCredentials_ShouldCreateUsableAuthenticatedSess response.StatusCode.Should().Be(HttpStatusCode.OK); - var result = await response.Content.ReadFromJsonAsync>(); + var result = await response.Content.ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.Items.Should().NotBeEmpty(); @@ -239,7 +237,7 @@ public async Task Login_AfterLockoutExpires_ShouldAllowCorrectCredentials() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"lockout-expiry-{Guid.NewGuid():N}"); @@ -284,7 +282,7 @@ await LoginAsync( [Fact] public async Task TryLogin_WithValidCredentials_ShouldReturnSuccessfulPreview() { - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"try-login-valid-{Guid.NewGuid():N}"); @@ -300,7 +298,7 @@ public async Task TryLogin_WithValidCredentials_ShouldReturnSuccessfulPreview() response.StatusCode.Should().Be(HttpStatusCode.OK); var result = - await response.Content.ReadFromJsonAsync(); + await response.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -327,7 +325,7 @@ public async Task TryLogin_WithValidCredentials_ShouldNotAuthenticateUser() response.StatusCode.Should().Be(HttpStatusCode.OK); - var result = await response.Content.ReadFromJsonAsync(); + var result = await response.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.IsSuccess.Should().BeTrue(); @@ -337,7 +335,7 @@ public async Task TryLogin_WithValidCredentials_ShouldNotAuthenticateUser() new GetProfileRequest { ProfileKey = null - }); + }, cancellationToken: TestContext.Current.CancellationToken); meResponse.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } @@ -345,7 +343,7 @@ public async Task TryLogin_WithValidCredentials_ShouldNotAuthenticateUser() [Fact] public async Task TryLogin_WithInvalidCredentials_ShouldReturnFailedPreviewWithoutSession() { - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"try-invalid-{Guid.NewGuid():N}"); @@ -361,7 +359,7 @@ public async Task TryLogin_WithInvalidCredentials_ShouldReturnFailedPreviewWitho response.StatusCode.Should().Be(HttpStatusCode.OK); var result = - await response.Content.ReadFromJsonAsync(); + await response.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.IsSuccess.Should().BeFalse(); @@ -387,7 +385,7 @@ public async Task TryLogin_WithMissingCredentials_ShouldReturnFailedPreview() response.StatusCode.Should().Be(HttpStatusCode.OK); - var result = await response.Content.ReadFromJsonAsync(); + var result = await response.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.IsSuccess.Should().BeFalse(); @@ -398,7 +396,7 @@ public async Task TryLogin_WithMissingCredentials_ShouldReturnFailedPreview() [Fact] public async Task Login_WithValidPreviewReceipt_ShouldAuthenticateUser() { - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"preview-valid-{Guid.NewGuid():N}"); @@ -414,7 +412,7 @@ public async Task Login_WithValidPreviewReceipt_ShouldAuthenticateUser() previewResponse.StatusCode.Should().Be(HttpStatusCode.OK); var preview = - await previewResponse.Content.ReadFromJsonAsync(); + await previewResponse.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); preview.Should().NotBeNull(); preview!.IsSuccess.Should().BeTrue(); @@ -441,7 +439,7 @@ public async Task Login_WithValidPreviewReceipt_ShouldAuthenticateUser() [Fact] public async Task Login_WithPreviewReceiptFromDifferentDevice_ShouldNotTrustReceipt() { - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var previewClient = CreateClient( $"receipt-owner-{Guid.NewGuid():N}"); @@ -457,7 +455,7 @@ public async Task Login_WithPreviewReceiptFromDifferentDevice_ShouldNotTrustRece previewResponse.StatusCode.Should().Be(HttpStatusCode.OK); var preview = await previewResponse.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); preview.Should().NotBeNull(); preview!.IsSuccess.Should().BeTrue(); @@ -474,7 +472,7 @@ public async Task Login_WithPreviewReceiptFromDifferentDevice_ShouldNotTrustRece identifier = user.Identifier, secret = "wrong-password", previewReceipt = preview.PreviewReceipt - }); + }, cancellationToken: TestContext.Current.CancellationToken); response.Headers .TryGetValues("Set-Cookie", out _) @@ -484,7 +482,7 @@ public async Task Login_WithPreviewReceiptFromDifferentDevice_ShouldNotTrustRece [Fact] public async Task Login_WithPreviewReceiptAndDifferentSecret_ShouldNotAuthenticate() { - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"preview-different-secret-{Guid.NewGuid():N}"); @@ -498,7 +496,7 @@ public async Task Login_WithPreviewReceiptAndDifferentSecret_ShouldNotAuthentica }); var preview = - await previewResponse.Content.ReadFromJsonAsync(); + await previewResponse.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); preview.Should().NotBeNull(); preview!.IsSuccess.Should().BeTrue(); @@ -511,7 +509,7 @@ public async Task Login_WithPreviewReceiptAndDifferentSecret_ShouldNotAuthentica identifier = user.Identifier, secret = "different-secret", previewReceipt = preview.PreviewReceipt - }); + }, cancellationToken: TestContext.Current.CancellationToken); loginResponse.Headers .TryGetValues("Set-Cookie", out _) @@ -521,8 +519,8 @@ public async Task Login_WithPreviewReceiptAndDifferentSecret_ShouldNotAuthentica [Fact] public async Task Login_WithPreviewReceiptAndDifferentIdentifier_ShouldFallBackToNormalAuthentication() { - var receiptOwner = await _factory.CreateLoginUserAsync(); - var otherUser = await _factory.CreateLoginUserAsync(); + var receiptOwner = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var otherUser = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"preview-different-identifier-{Guid.NewGuid():N}"); @@ -539,7 +537,7 @@ public async Task Login_WithPreviewReceiptAndDifferentIdentifier_ShouldFallBackT previewResponse.StatusCode.Should().Be(HttpStatusCode.OK); var preview = - await previewResponse.Content.ReadFromJsonAsync(); + await previewResponse.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); preview.Should().NotBeNull(); preview!.IsSuccess.Should().BeTrue(); @@ -573,8 +571,8 @@ public async Task Login_WithPreviewReceiptAndDifferentIdentifier_ShouldFallBackT [Fact] public async Task Login_WithPreviewReceiptFromDifferentIdentifier_ShouldNotBypassCredentialValidation() { - var receiptOwner = await _factory.CreateLoginUserAsync(); - var otherUser = await _factory.CreateLoginUserAsync(); + var receiptOwner = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var otherUser = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"preview-cross-user-{Guid.NewGuid():N}"); @@ -588,7 +586,7 @@ public async Task Login_WithPreviewReceiptFromDifferentIdentifier_ShouldNotBypas }); var preview = - await previewResponse.Content.ReadFromJsonAsync(); + await previewResponse.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); preview.Should().NotBeNull(); preview!.IsSuccess.Should().BeTrue(); @@ -613,7 +611,7 @@ public async Task Login_WithPreviewReceiptFromDifferentIdentifier_ShouldNotBypas [Fact] public async Task Login_WithUnknownPreviewReceipt_ShouldFallBackToNormalLoginValidation() { - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"unknown-receipt-{Guid.NewGuid():N}"); @@ -639,7 +637,7 @@ public async Task Login_WithUnknownPreviewReceipt_ShouldFallBackToNormalLoginVal [Fact] public async Task PreviewReceipt_AfterSuccessfulCommit_ShouldBeConsumed() { - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"preview-consume-{Guid.NewGuid():N}"); @@ -653,7 +651,7 @@ public async Task PreviewReceipt_AfterSuccessfulCommit_ShouldBeConsumed() }); var preview = - await previewResponse.Content.ReadFromJsonAsync(); + await previewResponse.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); preview.Should().NotBeNull(); preview!.IsSuccess.Should().BeTrue(); @@ -706,7 +704,7 @@ public async Task Login_WithRepeatedInvalidCredentials_ShouldLockAccount() { identifier = ValidIdentifier, secret = "wrong-password-1" - }); + }, cancellationToken: TestContext.Current.CancellationToken); first.Headers .TryGetValues("Set-Cookie", out _) @@ -731,7 +729,7 @@ public async Task Login_WithRepeatedInvalidCredentials_ShouldLockAccount() { identifier = ValidIdentifier, secret = ValidSecret - }); + }, cancellationToken: TestContext.Current.CancellationToken); correctLogin.Headers .TryGetValues("Set-Cookie", out _) @@ -753,7 +751,7 @@ public async Task TryLogin_WithRepeatedInvalidCredentials_ShouldParticipateInLoc }); var firstResult = - await first.Content.ReadFromJsonAsync(); + await first.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); firstResult.Should().NotBeNull(); firstResult!.IsSuccess.Should().BeFalse(); @@ -766,7 +764,7 @@ public async Task TryLogin_WithRepeatedInvalidCredentials_ShouldParticipateInLoc secret = "wrong-password-2" }); - var secondResult = await second.Content.ReadFromJsonAsync(); + var secondResult = await second.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); secondResult.Should().NotBeNull(); secondResult!.IsSuccess.Should().BeFalse(); @@ -781,7 +779,7 @@ public async Task TryLogin_WithRepeatedInvalidCredentials_ShouldParticipateInLoc }); var correctResult = - await correctAttempt.Content.ReadFromJsonAsync(); + await correctAttempt.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); correctResult.Should().NotBeNull(); correctResult!.IsSuccess.Should().BeFalse(); @@ -791,7 +789,7 @@ public async Task TryLogin_WithRepeatedInvalidCredentials_ShouldParticipateInLoc [Fact] public async Task TryLogin_WithValidCredentials_ShouldNotConsumeFailureAttempt() { - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"try-no-failure-{Guid.NewGuid():N}"); @@ -805,7 +803,7 @@ public async Task TryLogin_WithValidCredentials_ShouldNotConsumeFailureAttempt() }); var previewResult = - await preview.Content.ReadFromJsonAsync(); + await preview.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); previewResult.Should().NotBeNull(); previewResult!.IsSuccess.Should().BeTrue(); @@ -819,7 +817,7 @@ public async Task TryLogin_WithValidCredentials_ShouldNotConsumeFailureAttempt() }); var failureResult = - await failure.Content.ReadFromJsonAsync(); + await failure.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); failureResult.Should().NotBeNull(); failureResult!.IsSuccess.Should().BeFalse(); @@ -829,7 +827,7 @@ public async Task TryLogin_WithValidCredentials_ShouldNotConsumeFailureAttempt() [Fact] public async Task PreviewReceipt_WithDifferentSecret_ShouldNotSuppressFailureAccounting() { - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"receipt-accounting-{Guid.NewGuid():N}"); @@ -845,7 +843,7 @@ public async Task PreviewReceipt_WithDifferentSecret_ShouldNotSuppressFailureAcc previewResponse.StatusCode.Should().Be(HttpStatusCode.OK); var preview = - await previewResponse.Content.ReadFromJsonAsync(); + await previewResponse.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); preview.Should().NotBeNull(); preview!.IsSuccess.Should().BeTrue(); @@ -860,7 +858,7 @@ public async Task PreviewReceipt_WithDifferentSecret_ShouldNotSuppressFailureAcc identifier = user.Identifier, secret = "wrong-password-1", previewReceipt = preview.PreviewReceipt - }); + }, cancellationToken: TestContext.Current.CancellationToken); firstFailure.Headers .TryGetValues("Set-Cookie", out _) @@ -874,7 +872,7 @@ public async Task PreviewReceipt_WithDifferentSecret_ShouldNotSuppressFailureAcc { identifier = user.Identifier, secret = "wrong-password-2" - }); + }, cancellationToken: TestContext.Current.CancellationToken); secondFailure.Headers .TryGetValues("Set-Cookie", out _) @@ -887,7 +885,7 @@ public async Task PreviewReceipt_WithDifferentSecret_ShouldNotSuppressFailureAcc { identifier = user.Identifier, secret = user.Secret - }); + }, cancellationToken: TestContext.Current.CancellationToken); correctLogin.Headers .TryGetValues("Set-Cookie", out _) @@ -897,7 +895,7 @@ public async Task PreviewReceipt_WithDifferentSecret_ShouldNotSuppressFailureAcc [Fact] public async Task PreviewReceipt_FromDifferentDevice_ShouldNotSuppressFailureAccounting() { - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var receiptClient = CreateClient( $"receipt-device-a-{Guid.NewGuid():N}"); @@ -911,7 +909,7 @@ public async Task PreviewReceipt_FromDifferentDevice_ShouldNotSuppressFailureAcc }); var preview = - await previewResponse.Content.ReadFromJsonAsync(); + await previewResponse.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); preview.Should().NotBeNull(); preview!.IsSuccess.Should().BeTrue(); @@ -929,7 +927,7 @@ public async Task PreviewReceipt_FromDifferentDevice_ShouldNotSuppressFailureAcc identifier = user.Identifier, secret = "wrong-password-1", previewReceipt = preview.PreviewReceipt - }); + }, cancellationToken: TestContext.Current.CancellationToken); firstFailure.Headers .TryGetValues("Set-Cookie", out _) @@ -943,7 +941,7 @@ public async Task PreviewReceipt_FromDifferentDevice_ShouldNotSuppressFailureAcc { identifier = user.Identifier, secret = "wrong-password-2" - }); + }, cancellationToken: TestContext.Current.CancellationToken); secondFailure.Headers .TryGetValues("Set-Cookie", out _) @@ -955,7 +953,7 @@ public async Task PreviewReceipt_FromDifferentDevice_ShouldNotSuppressFailureAcc { identifier = user.Identifier, secret = user.Secret - }); + }, cancellationToken: TestContext.Current.CancellationToken); correctLogin.Headers .TryGetValues("Set-Cookie", out _) @@ -965,7 +963,7 @@ public async Task PreviewReceipt_FromDifferentDevice_ShouldNotSuppressFailureAcc [Fact] public async Task SuccessfulLogin_ShouldResetPreviousFailureAccounting() { - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"success-reset-{Guid.NewGuid():N}"); @@ -1008,7 +1006,7 @@ public async Task SuccessfulLogin_ShouldResetPreviousFailureAccounting() secret = "wrong-password-2" }); - var result = await failureAfterSuccess.Content.ReadFromJsonAsync(); + var result = await failureAfterSuccess.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.IsSuccess.Should().BeFalse(); @@ -1027,7 +1025,7 @@ await attackerDevice.PostAsJsonAsync( { identifier = ValidIdentifier, secret = "wrong-password-1" - }); + }, cancellationToken: TestContext.Current.CancellationToken); await attackerDevice.PostAsJsonAsync( "/auth/login", @@ -1035,7 +1033,7 @@ await attackerDevice.PostAsJsonAsync( { identifier = ValidIdentifier, secret = "wrong-password-2" - }); + }, cancellationToken: TestContext.Current.CancellationToken); using var differentDevice = CreateClient( "lockout-other-device-000000000000000"); @@ -1046,7 +1044,7 @@ await attackerDevice.PostAsJsonAsync( { identifier = ValidIdentifier, secret = ValidSecret - }); + }, cancellationToken: TestContext.Current.CancellationToken); response.Headers .TryGetValues("Set-Cookie", out _) @@ -1058,7 +1056,7 @@ await attackerDevice.PostAsJsonAsync( [Fact] public async Task Login_WithFormPayload_ShouldAuthenticateUser() { - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"form-login-{Guid.NewGuid():N}"); @@ -1086,7 +1084,7 @@ public async Task Login_WithFormPayload_ShouldAuthenticateUser() [Fact] public async Task TryLogin_WithFormPayload_ShouldReturnSuccessfulPreview() { - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"form-try-login-{Guid.NewGuid():N}"); @@ -1105,7 +1103,7 @@ public async Task TryLogin_WithFormPayload_ShouldReturnSuccessfulPreview() response.StatusCode.Should().Be(HttpStatusCode.OK); var result = - await response.Content.ReadFromJsonAsync(); + await response.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.IsSuccess.Should().BeTrue(); @@ -1161,7 +1159,7 @@ public async Task TryLogin_WithMissingOrWhitespaceCredentials_ShouldReturnInvali response.StatusCode.Should().Be(HttpStatusCode.OK); - var result = await response.Content.ReadFromJsonAsync(); + var result = await response.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.IsSuccess.Should().BeFalse(); @@ -1180,7 +1178,7 @@ public async Task TryLogin_WithUnsupportedContentType_ShouldReturnBadRequest() System.Text.Encoding.UTF8, "text/plain"); - var response = await client.PostAsync("/auth/try-login", content); + var response = await client.PostAsync("/auth/try-login", content, TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); @@ -1200,7 +1198,7 @@ public async Task Login_WithUnsupportedContentType_ShouldNotAuthenticate() System.Text.Encoding.UTF8, "text/plain"); - var response = await client.PostAsync("/auth/login", content); + var response = await client.PostAsync("/auth/login", content, TestContext.Current.CancellationToken); response.Headers .TryGetValues("Set-Cookie", out _) @@ -1222,7 +1220,7 @@ public async Task TryLogin_WithEmptyJsonObject_ShouldReturnInvalidCredentials() response.StatusCode.Should().Be(HttpStatusCode.OK); - var result = await response.Content.ReadFromJsonAsync(); + var result = await response.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.IsSuccess.Should().BeFalse(); @@ -1238,7 +1236,7 @@ public async Task Login_WithEmptyJsonObject_ShouldNotAuthenticate() var response = await client.PostAsJsonAsync( "/auth/login", - new { }); + new { }, cancellationToken: TestContext.Current.CancellationToken); response.Headers .TryGetValues("Set-Cookie", out _) @@ -1251,7 +1249,7 @@ public async Task Login_WithEmptyJsonObject_ShouldNotAuthenticate() [Fact] public async Task Login_WithJsonPropertyNamesUsingDifferentCasing_ShouldAuthenticate() { - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"json-casing-{Guid.NewGuid():N}"); @@ -1297,7 +1295,7 @@ public async Task Login_WithUnknownIdentifier_ShouldNotRevealWhetherUserExists() { identifier = $"unknown-{Guid.NewGuid():N}", secret = "wrong-password" - }); + }, cancellationToken: TestContext.Current.CancellationToken); var existingResponse = await existingUserClient.PostAsJsonAsync( "/auth/login", @@ -1305,7 +1303,7 @@ public async Task Login_WithUnknownIdentifier_ShouldNotRevealWhetherUserExists() { identifier = ValidIdentifier, secret = "wrong-password" - }); + }, cancellationToken: TestContext.Current.CancellationToken); unknownResponse.StatusCode.Should().Be(existingResponse.StatusCode); @@ -1321,7 +1319,7 @@ public async Task Login_WithUnknownIdentifier_ShouldNotRevealWhetherUserExists() [Fact] public async Task TryLogin_WithUnknownIdentifier_ShouldReturnSameFailureReasonAsWrongPassword() { - var existingUser = await _factory.CreateLoginUserAsync(); + var existingUser = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var unknownClient = CreateClient( $"unknown-{Guid.NewGuid():N}"); @@ -1349,10 +1347,10 @@ public async Task TryLogin_WithUnknownIdentifier_ShouldReturnSameFailureReasonAs existingResponse.StatusCode.Should().Be(HttpStatusCode.OK); var unknownResult = - await unknownResponse.Content.ReadFromJsonAsync(); + await unknownResponse.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); var existingResult = - await existingResponse.Content.ReadFromJsonAsync(); + await existingResponse.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); unknownResult.Should().NotBeNull(); existingResult.Should().NotBeNull(); @@ -1387,7 +1385,7 @@ public async Task TryLogin_WithUnknownIdentifier_ShouldNotIssuePreviewReceipt() response.StatusCode.Should().Be(HttpStatusCode.OK); var result = - await response.Content.ReadFromJsonAsync(); + await response.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -1403,7 +1401,7 @@ public async Task TryLogin_WithUnknownIdentifier_ShouldNotIssuePreviewReceipt() [Fact] public async Task TryLogin_WithWrongPassword_ShouldNotIssuePreviewReceipt() { - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"wrong-password-{Guid.NewGuid():N}"); @@ -1419,7 +1417,7 @@ public async Task TryLogin_WithWrongPassword_ShouldNotIssuePreviewReceipt() response.StatusCode.Should().Be(HttpStatusCode.OK); var result = - await response.Content.ReadFromJsonAsync(); + await response.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -1444,7 +1442,7 @@ public async Task Login_WithUnknownIdentifier_ShouldNotReturnAuthenticationCrede { identifier = $"unknown-{Guid.NewGuid():N}", secret = ValidSecret - }); + }, cancellationToken: TestContext.Current.CancellationToken); response.Headers .TryGetValues("Set-Cookie", out _) @@ -1461,7 +1459,7 @@ public async Task Login_FailuresOutsideFailureWindow_ShouldNotAccumulateTowardLo { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"failure-window-{Guid.NewGuid():N}"); @@ -1511,7 +1509,7 @@ public async Task Login_BeforeLockoutExpires_ShouldRemainLocked() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"active-lockout-{Guid.NewGuid():N}"); @@ -1544,7 +1542,7 @@ public async Task Login_AtLockoutExpirationBoundary_ShouldAllowCorrectCredential { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"lockout-boundary-{Guid.NewGuid():N}"); @@ -1579,7 +1577,7 @@ public async Task Login_FailuresWithinFailureWindow_ShouldAccumulateTowardLockou { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"failure-window-inside-{Guid.NewGuid():N}"); @@ -1612,7 +1610,7 @@ public async Task Login_FailureDuringLockout_ShouldNotExtendLockout_WhenExtendLo { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"lockout-no-extension-{Guid.NewGuid():N}"); @@ -1671,7 +1669,7 @@ public async Task ConcurrentLoginFailures_ShouldNotLoseFailureAttempts() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client1 = CreateClient( $"concurrent-failure-1-{Guid.NewGuid():N}"); @@ -1712,7 +1710,7 @@ public async Task ConcurrentSuccessfulLogins_FromDifferentDevices_ShouldCreateUs { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device1 = $"concurrent-success-1-{Guid.NewGuid():N}"; var device2 = $"concurrent-success-2-{Guid.NewGuid():N}"; @@ -1745,7 +1743,7 @@ public async Task ConcurrentSuccessfulLogins_FromDifferentDevices_ShouldCreateUs { PageNumber = 1, PageSize = 10 - }), + }, cancellationToken: TestContext.Current.CancellationToken), authenticatedClient2.PostAsJsonAsync( "/auth/me/sessions/chains", @@ -1753,7 +1751,7 @@ public async Task ConcurrentSuccessfulLogins_FromDifferentDevices_ShouldCreateUs { PageNumber = 1, PageSize = 10 - })); + }, cancellationToken: TestContext.Current.CancellationToken)); sessionResponses.Should().OnlyContain( x => x.StatusCode == HttpStatusCode.OK); @@ -1761,12 +1759,12 @@ public async Task ConcurrentSuccessfulLogins_FromDifferentDevices_ShouldCreateUs var result1 = await sessionResponses[0] .Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); var result2 = await sessionResponses[1] .Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); result1.Should().NotBeNull(); result2.Should().NotBeNull(); @@ -1787,7 +1785,7 @@ public async Task ConcurrentLogin_WithSamePreviewReceipt_ShouldNotAllowReceiptTo { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device = $"preview-concurrent-{Guid.NewGuid():N}"; diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/LogoutAdminTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/LogoutAdminTests.cs index fb873146..1f18607e 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/LogoutAdminTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/LogoutAdminTests.cs @@ -23,7 +23,7 @@ public async Task LogoutDeviceAdmin_WithoutAuthentication_ShouldReturnUnauthoriz { _factory.Clock.Reset(); - var target = await _factory.CreateLoginUserAsync(); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var targetClient = CreateClient( $"admin-logout-device-target-{Guid.NewGuid():N}"); @@ -44,7 +44,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var chains = await chainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); chains.Should().NotBeNull(); @@ -76,8 +76,8 @@ public async Task LogoutDeviceAdmin_WithoutRequiredPermission_ShouldReturnForbid { _factory.Clock.Reset(); - var actor = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var actor = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var actorClient = CreateClient( $"admin-logout-device-unprivileged-{Guid.NewGuid():N}"); @@ -112,7 +112,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var targetChains = await targetChainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); targetChains.Should().NotBeNull(); @@ -147,7 +147,7 @@ public async Task LogoutAllAdmin_WithoutAuthentication_ShouldReturnUnauthorized( { _factory.Clock.Reset(); - var target = await _factory.CreateLoginUserAsync(); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var targetClient = CreateClient( $"admin-logout-all-target-{Guid.NewGuid():N}"); @@ -185,8 +185,8 @@ public async Task LogoutAllAdmin_WithoutRequiredPermission_ShouldReturnForbidden { _factory.Clock.Reset(); - var actor = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var actor = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var actorClient = CreateClient( $"admin-logout-all-unprivileged-{Guid.NewGuid():N}"); @@ -238,14 +238,14 @@ public async Task LogoutDeviceAdmin_WithPermission_ShouldInvalidateTargetDeviceS { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Flows.LogoutDeviceAdmin - ]); + ], TestContext.Current.CancellationToken); var adminDevice = $"logout-admin-{Guid.NewGuid():N}"; @@ -289,7 +289,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var targetChains = await targetChainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); targetChains.Should().NotBeNull(); @@ -333,14 +333,14 @@ public async Task LogoutDeviceAdmin_ShouldNotInvalidateTargetUsersOtherDevice() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Flows.LogoutDeviceAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"logout-admin-{Guid.NewGuid():N}"); @@ -391,7 +391,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var chains = await chainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); chains.Should().NotBeNull(); @@ -438,15 +438,15 @@ public async Task LogoutDeviceAdmin_ShouldNotAllowChainFromDifferentTargetUser() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var targetA = await _factory.CreateLoginUserAsync(); - var targetB = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var targetA = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var targetB = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Flows.LogoutDeviceAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"logout-admin-{Guid.NewGuid():N}"); @@ -495,7 +495,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var targetBChains = await targetBChainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); targetBChains.Should().NotBeNull(); @@ -536,14 +536,14 @@ public async Task LogoutOthersAdmin_ShouldLogoutAllTargetDevicesExceptSpecifiedC { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Flows.LogoutOthersAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient($"logout-others-admin-{Guid.NewGuid():N}"); @@ -570,7 +570,7 @@ await _factory.GrantPermissionsAsync( chainsResponse.StatusCode.Should().Be(HttpStatusCode.OK); - var chains = await chainsResponse.Content.ReadFromJsonAsync>(); + var chains = await chainsResponse.Content.ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); chains.Should().NotBeNull(); @@ -599,8 +599,8 @@ public async Task LogoutOthersAdmin_WithoutRequiredPermission_ShouldReturnForbid { _factory.Clock.Reset(); - var actor = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var actor = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var actorClient = CreateClient( $"logout-others-actor-{Guid.NewGuid():N}"); @@ -637,7 +637,7 @@ await LoginAsync( await GetChainsAsync(targetClient1); var chains = await chainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); chains.Should().NotBeNull(); @@ -675,15 +675,15 @@ public async Task LogoutOthersAdmin_ShouldRejectCurrentChainFromDifferentUser() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); - var otherUser = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var otherUser = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Flows.LogoutOthersAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"logout-others-admin-{Guid.NewGuid():N}"); @@ -733,7 +733,7 @@ await LoginAsync( await GetChainsAsync(otherClient); var otherChains = await otherChainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); otherChains.Should().NotBeNull(); @@ -780,14 +780,14 @@ public async Task LogoutOthersAdmin_WithSingleTargetDevice_ShouldPreserveThatDev { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Flows.LogoutOthersAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"logout-others-admin-{Guid.NewGuid():N}"); @@ -822,7 +822,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var chains = await chainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); chains.Should().NotBeNull(); @@ -860,14 +860,14 @@ public async Task LogoutOthersAdmin_WhenRepeated_ShouldRemainIdempotent() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Flows.LogoutOthersAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"logout-others-admin-{Guid.NewGuid():N}"); @@ -912,7 +912,7 @@ await LoginAsync( await GetChainsAsync(targetClient1); var chains = await chainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); chains.Should().NotBeNull(); @@ -968,14 +968,14 @@ public async Task LogoutAllAdmin_ShouldLogoutAllTargetDevices() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Flows.LogoutAllAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"logout-all-admin-{Guid.NewGuid():N}"); @@ -1075,14 +1075,14 @@ public async Task LogoutAllAdmin_ShouldNotInvalidateAdminSession() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Flows.LogoutAllAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"logout-all-admin-{Guid.NewGuid():N}"); @@ -1138,8 +1138,8 @@ public async Task LogoutAllAdmin_WithoutRequiredPermission_ShouldReturnForbidden { _factory.Clock.Reset(); - var actor = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var actor = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var actorClient = CreateClient( $"logout-all-actor-{Guid.NewGuid():N}"); @@ -1226,14 +1226,14 @@ public async Task LogoutAllAdmin_WhenRepeated_ShouldRemainIdempotent() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Flows.LogoutAllAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"logout-all-admin-{Guid.NewGuid():N}"); @@ -1310,15 +1310,15 @@ public async Task LogoutAllAdmin_ShouldNotAffectUnrelatedUser() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); - var unrelated = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var unrelated = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Flows.LogoutAllAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"logout-all-admin-{Guid.NewGuid():N}"); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/LogoutTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/LogoutTests.cs index 850f7be3..76abc819 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/LogoutTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/LogoutTests.cs @@ -23,7 +23,7 @@ public async Task Logout_WithAuthenticatedSession_ShouldSucceed() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"logout-basic-{Guid.NewGuid():N}"; using var client = CreateClient(deviceId); @@ -51,7 +51,7 @@ public async Task Logout_ShouldDeleteSessionCookie() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"logout-cookie-{Guid.NewGuid():N}"; using var client = CreateClient(deviceId); @@ -93,7 +93,7 @@ public async Task Logout_ShouldInvalidateSessionOnServer() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"logout-invalidation-{Guid.NewGuid():N}"; using var client = CreateClient(deviceId); @@ -131,7 +131,7 @@ public async Task Logout_OldSessionCookie_ShouldNotAuthenticateFromNewClient() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"logout-replay-{Guid.NewGuid():N}"; using var originalClient = CreateClient(deviceId); @@ -175,7 +175,7 @@ public async Task Logout_ShouldNotInvalidateOtherDeviceSession() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device1 = $"logout-device-1-{Guid.NewGuid():N}"; var device2 = $"logout-device-2-{Guid.NewGuid():N}"; @@ -236,8 +236,8 @@ public async Task Logout_ShouldNotInvalidateAnotherUsersSession() { _factory.Clock.Reset(); - var user1 = await _factory.CreateLoginUserAsync(); - var user2 = await _factory.CreateLoginUserAsync(); + var user1 = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var user2 = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client1 = CreateClient( $"logout-user-1-{Guid.NewGuid():N}"); @@ -289,7 +289,7 @@ public async Task Logout_ShouldDetachActiveSessionFromCurrentChain() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device = $"logout-detach-{Guid.NewGuid():N}"; using var client = CreateClient(device); @@ -305,11 +305,11 @@ public async Task Logout_ShouldDetachActiveSessionFromCurrentChain() { PageNumber = 1, PageSize = 50 - }); + }, cancellationToken: TestContext.Current.CancellationToken); beforeResponse.StatusCode.Should().Be(HttpStatusCode.OK); - var before = await beforeResponse.Content.ReadFromJsonAsync>(); + var before = await beforeResponse.Content.ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); before.Should().NotBeNull(); @@ -325,7 +325,7 @@ public async Task Logout_ShouldDetachActiveSessionFromCurrentChain() logout.StatusCode.Should().Be(HttpStatusCode.Found); - var oldSessionResponse = await client.PostAsJsonAsync("/auth/me/sessions/chains", new PageRequest()); + var oldSessionResponse = await client.PostAsJsonAsync("/auth/me/sessions/chains", new PageRequest(), cancellationToken: TestContext.Current.CancellationToken); oldSessionResponse.StatusCode.Should().Be(HttpStatusCode.Unauthorized); @@ -341,11 +341,11 @@ public async Task Logout_ShouldDetachActiveSessionFromCurrentChain() { PageNumber = 1, PageSize = 50 - }); + }, cancellationToken: TestContext.Current.CancellationToken); afterResponse.StatusCode.Should().Be(HttpStatusCode.OK); - var after = await afterResponse.Content.ReadFromJsonAsync>(); + var after = await afterResponse.Content.ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); after.Should().NotBeNull(); var currentAfter = after!.Items.Single(x => x.IsCurrentDevice); @@ -363,7 +363,7 @@ public async Task Logout_OldSession_ShouldRemainInvalidAfterLoginAgain() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device = $"logout-old-session-{Guid.NewGuid():N}"; using var firstClient = CreateClient(device); @@ -387,14 +387,14 @@ public async Task Logout_OldSession_ShouldRemainInvalidAfterLoginAgain() secondClient.DefaultRequestHeaders.Add("Cookie", newCookie); - var newSessionResponse = await secondClient.PostAsJsonAsync("/auth/me/sessions/chains", new PageRequest()); + var newSessionResponse = await secondClient.PostAsJsonAsync("/auth/me/sessions/chains", new PageRequest(), cancellationToken: TestContext.Current.CancellationToken); newSessionResponse.StatusCode.Should().Be(HttpStatusCode.OK); using var replayClient = CreateClient(device); replayClient.DefaultRequestHeaders.Add("Cookie", oldCookie); - var replayResponse = await replayClient.PostAsJsonAsync("/auth/me/sessions/chains", new PageRequest()); + var replayResponse = await replayClient.PostAsJsonAsync("/auth/me/sessions/chains", new PageRequest(), cancellationToken: TestContext.Current.CancellationToken); replayResponse.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } @@ -403,7 +403,7 @@ public async Task Logout_ShouldDetachOnlyCurrentDeviceSession() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device1 = $"logout-device-1-{Guid.NewGuid():N}"; var device2 = $"logout-device-2-{Guid.NewGuid():N}"; @@ -438,7 +438,7 @@ public async Task Logout_ShouldDetachOnlyCurrentDeviceSession() beforeResponse.StatusCode.Should().Be(HttpStatusCode.OK); var before = await beforeResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); before.Should().NotBeNull(); @@ -469,7 +469,7 @@ public async Task Logout_ShouldDetachOnlyCurrentDeviceSession() device2Result.StatusCode.Should().Be(HttpStatusCode.OK); - var after = await device2Result.Content.ReadFromJsonAsync>(); + var after = await device2Result.Content.ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); after.Should().NotBeNull(); @@ -485,7 +485,7 @@ public async Task Logout_ShouldNotRevokeDeviceChain() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device = $"logout-not-revoke-{Guid.NewGuid():N}"; using var client = CreateClient(device); @@ -507,7 +507,7 @@ public async Task Logout_ShouldNotRevokeDeviceChain() }); var before = await beforeResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); before.Should().NotBeNull(); @@ -539,7 +539,7 @@ public async Task Logout_ShouldNotRevokeDeviceChain() }); var after = await afterResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); after.Should().NotBeNull(); @@ -575,7 +575,7 @@ public async Task Logout_WithAlreadyLoggedOutSession_ShouldRemainIdempotent() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device = $"logout-idempotent-{Guid.NewGuid():N}"; using var client = CreateClient(device); @@ -618,7 +618,7 @@ public async Task Logout_WithInvalidSessionCookie_ShouldNotAffectValidSession() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var validDevice = $"logout-valid-{Guid.NewGuid():N}"; @@ -669,7 +669,7 @@ public async Task Logout_WithInvalidSessionCookie_ShouldNotAffectValidSession() validSession.StatusCode.Should().Be(HttpStatusCode.OK); var chains = await validSession.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); chains.Should().NotBeNull(); @@ -684,7 +684,7 @@ public async Task Logout_WithOldSessionAfterRelogin_ShouldNotLogoutNewSession() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device = $"logout-stale-{Guid.NewGuid():N}"; // @@ -742,7 +742,7 @@ public async Task Logout_WithOldSessionAfterRelogin_ShouldNotLogoutNewSession() beforeReplay.StatusCode.Should().Be(HttpStatusCode.OK); var beforeChains = await beforeReplay.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); beforeChains.Should().NotBeNull(); @@ -783,7 +783,7 @@ public async Task Logout_WithOldSessionAfterRelogin_ShouldNotLogoutNewSession() afterReplay.StatusCode.Should().Be(HttpStatusCode.OK); var afterChains = await afterReplay.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); afterChains.Should().NotBeNull(); @@ -800,7 +800,7 @@ public async Task RepeatedLogoutLoginCycles_ShouldPreserveChainAndRotateSessions { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device = $"logout-cycle-{Guid.NewGuid():N}"; SessionChainId? expectedChainId = null; @@ -834,7 +834,7 @@ public async Task RepeatedLogoutLoginCycles_ShouldPreserveChainAndRotateSessions chainsResponse.StatusCode.Should().Be(HttpStatusCode.OK); var chains = await chainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); chains.Should().NotBeNull(); @@ -880,7 +880,7 @@ public async Task Logout_WithStaleCredential_ShouldNotAffectOtherDevice() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device1 = $"logout-stale-device-1-{Guid.NewGuid():N}"; @@ -930,7 +930,7 @@ await CsrfTestClient.PostJsonAsync(device2Client, .Be(HttpStatusCode.OK); var before = await device2BeforeResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); before.Should().NotBeNull(); @@ -968,7 +968,7 @@ await CsrfTestClient.PostJsonAsync(device2Client, .Be(HttpStatusCode.OK); var after = await device2AfterResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); after.Should().NotBeNull(); @@ -985,7 +985,7 @@ public async Task Logout_ShouldNotReturnAuthenticationCredentials() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device = $"logout-no-credentials-{Guid.NewGuid():N}"; using var client = CreateClient(device); @@ -1042,7 +1042,7 @@ public async Task Logout_ShouldDeleteSessionCookieWithRootPath() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device = $"logout-cookie-path-{Guid.NewGuid():N}"; using var client = CreateClient(device); @@ -1088,7 +1088,7 @@ public async Task Logout_ShouldNotReissueSessionCookieAfterServerSideInvalidatio { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device = $"logout-no-reissue-{Guid.NewGuid():N}"; using var client = CreateClient(device); @@ -1158,7 +1158,7 @@ public async Task Logout_CookieDeletion_ShouldNotContainOriginalSessionCredentia { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device = $"logout-cookie-value-{Guid.NewGuid():N}"; using var client = CreateClient(device); @@ -1210,7 +1210,7 @@ public async Task ConcurrentLogout_WithSameSession_ShouldLeaveSessionInvalid() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device = $"logout-concurrent-same-{Guid.NewGuid():N}"; using var loginClient = CreateClient(device); @@ -1241,7 +1241,7 @@ public async Task ConcurrentLogout_WithSameSession_ShouldLeaveSessionInvalid() { if (response.StatusCode == HttpStatusCode.BadRequest) { - var body = await response.Content.ReadAsStringAsync(); + var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); body.Should().Contain("invalid_antiforgery_token"); continue; @@ -1275,7 +1275,7 @@ public async Task ConcurrentLogout_FromDifferentDevices_ShouldInvalidateBothSess { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device1 = $"logout-concurrent-device-1-{Guid.NewGuid():N}"; @@ -1347,7 +1347,7 @@ public async Task ConcurrentRequests_WithSessionBeingLoggedOut_ShouldNeverLeaveC { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device = $"logout-concurrent-request-{Guid.NewGuid():N}"; @@ -1420,7 +1420,7 @@ public async Task ConcurrentLogoutAndRelogin_OnSameDevice_ShouldLeaveChainInCons { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device = $"logout-relogin-race-{Guid.NewGuid():N}"; @@ -1558,7 +1558,7 @@ public async Task LogoutDeviceSelf_ShouldRevokeTargetDeviceSession() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device1 = $"logout-device-self-1-{Guid.NewGuid():N}"; var device2 = $"logout-device-self-2-{Guid.NewGuid():N}"; @@ -1580,7 +1580,7 @@ public async Task LogoutDeviceSelf_ShouldRevokeTargetDeviceSession() chainsResponse.StatusCode.Should().Be(HttpStatusCode.OK); var page = await chainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); page.Should().NotBeNull(); @@ -1611,7 +1611,7 @@ public async Task LogoutDeviceSelf_ShouldNotInvalidateCurrentDevice_WhenAnotherD { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device1 = $"logout-device-keep-{Guid.NewGuid():N}"; var device2 = $"logout-device-target-{Guid.NewGuid():N}"; @@ -1631,7 +1631,7 @@ public async Task LogoutDeviceSelf_ShouldNotInvalidateCurrentDevice_WhenAnotherD var chainsResponse = await GetChainsAsync(client1); var page = await chainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); page.Should().NotBeNull(); @@ -1679,7 +1679,7 @@ public async Task LogoutDeviceSelf_ShouldDetachActiveSessionWithoutRevokingChain { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device1 = $"logout-device-observer-{Guid.NewGuid():N}"; var device2 = $"logout-device-revoked-{Guid.NewGuid():N}"; @@ -1696,7 +1696,7 @@ public async Task LogoutDeviceSelf_ShouldDetachActiveSessionWithoutRevokingChain var beforeResponse = await GetChainsAsync(client1); - var before = await beforeResponse.Content.ReadFromJsonAsync>(); + var before = await beforeResponse.Content.ReadFromJsonAsync>(TestContext.Current.CancellationToken); before.Should().NotBeNull(); @@ -1717,7 +1717,7 @@ public async Task LogoutDeviceSelf_ShouldDetachActiveSessionWithoutRevokingChain afterResponse.StatusCode.Should().Be(HttpStatusCode.OK); - var after = await afterResponse.Content.ReadFromJsonAsync>(); + var after = await afterResponse.Content.ReadFromJsonAsync>(TestContext.Current.CancellationToken); after.Should().NotBeNull(); @@ -1751,8 +1751,8 @@ public async Task LogoutDeviceSelf_ShouldNotAllowUserToLogoutAnotherUsersDevice( { _factory.Clock.Reset(); - var attacker = await _factory.CreateLoginUserAsync(); - var victim = await _factory.CreateLoginUserAsync(); + var attacker = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var victim = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var attackerDevice = $"logout-device-attacker-{Guid.NewGuid():N}"; @@ -1793,7 +1793,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var victimChains = await victimChainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); victimChains.Should().NotBeNull(); @@ -1825,7 +1825,7 @@ public async Task LogoutDeviceSelf_ShouldAllowCurrentDeviceToLogoutItself() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device = $"logout-device-current-{Guid.NewGuid():N}"; using var client = CreateClient(device); @@ -1848,7 +1848,7 @@ public async Task LogoutDeviceSelf_ShouldAllowCurrentDeviceToLogoutItself() chainsResponse.StatusCode.Should().Be(HttpStatusCode.OK); var chains = await chainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); chains.Should().NotBeNull(); @@ -1880,7 +1880,7 @@ public async Task LogoutDeviceSelf_WithUnknownChain_ShouldNotAffectCurrentSessio { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device = $"logout-device-unknown-{Guid.NewGuid():N}"; using var client = CreateClient(device); @@ -1926,7 +1926,7 @@ public async Task LogoutDeviceSelf_AlreadyLoggedOutDevice_ShouldRemainSafe() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var actorDevice = $"logout-device-repeat-actor-{Guid.NewGuid():N}"; @@ -1960,7 +1960,7 @@ await LoginAsync( var chainsResponse = await GetChainsAsync(actorClient); var chains = await chainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); chains.Should().NotBeNull(); @@ -2021,7 +2021,7 @@ public async Task LogoutDeviceSelf_LoggedOutDevice_ShouldBeAbleToLoginAgain() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var actorDevice = $"logout-device-relogin-actor-{Guid.NewGuid():N}"; @@ -2056,7 +2056,7 @@ await LoginAsync( await GetChainsAsync(actorClient); var chains = await chainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); chains.Should().NotBeNull(); @@ -2122,7 +2122,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var after = await afterResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); after.Should().NotBeNull(); @@ -2146,7 +2146,7 @@ public async Task LogoutOthersSelf_ShouldInvalidateAllOtherDeviceSessions() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device1 = $"logout-others-current-{Guid.NewGuid():N}"; var device2 = $"logout-others-other-1-{Guid.NewGuid():N}"; @@ -2203,7 +2203,7 @@ public async Task LogoutOthersSelf_ShouldKeepCurrentSessionValid() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var currentDevice = $"logout-others-keep-current-{Guid.NewGuid():N}"; @@ -2244,7 +2244,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var before = await beforeResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); before.Should().NotBeNull(); @@ -2272,7 +2272,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var after = await afterResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); after.Should().NotBeNull(); @@ -2301,7 +2301,7 @@ public async Task LogoutOthersSelf_ShouldDetachOtherSessionsWithoutRevokingChain { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var actorDevice = $"logout-others-observer-{Guid.NewGuid():N}"; @@ -2353,7 +2353,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var before = await beforeResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); before.Should().NotBeNull(); @@ -2385,7 +2385,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var after = await afterResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); after.Should().NotBeNull(); @@ -2420,8 +2420,8 @@ public async Task LogoutOthersSelf_ShouldNotAffectAnotherUsersSessions() { _factory.Clock.Reset(); - var actor = await _factory.CreateLoginUserAsync(); - var victim = await _factory.CreateLoginUserAsync(); + var actor = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var victim = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); // // Actor owns two devices so logout-others actually has work to do. @@ -2515,7 +2515,7 @@ public async Task LogoutOthersSelf_WithNoOtherSessions_ShouldSucceed() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device = $"logout-others-single-{Guid.NewGuid():N}"; @@ -2539,7 +2539,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var before = await beforeResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); before.Should().NotBeNull(); @@ -2567,7 +2567,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var after = await afterResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); after.Should().NotBeNull(); @@ -2586,7 +2586,7 @@ public async Task LogoutOthersSelf_RepeatedCall_ShouldRemainSafe() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var actorClient = CreateClient( $"logout-others-repeat-actor-{Guid.NewGuid():N}"); @@ -2643,7 +2643,7 @@ public async Task LogoutOthersSelf_OldCredentialsFromOtherDevices_ShouldRemainIn { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var actorDevice = $"logout-others-replay-actor-{Guid.NewGuid():N}"; @@ -2710,7 +2710,7 @@ public async Task LogoutOthersSelf_LoggedOutDevices_ShouldBeAbleToLoginAgain() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var actorDevice = $"logout-others-relogin-actor-{Guid.NewGuid():N}"; @@ -2748,7 +2748,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var before = await beforeResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); before.Should().NotBeNull(); @@ -2813,7 +2813,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var after = await afterResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); after.Should().NotBeNull(); @@ -2847,7 +2847,7 @@ public async Task ConcurrentLogoutOthersSelf_ShouldLeaveOnlyActorSessionUsable() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var actorDevice = $"logout-others-concurrent-actor-{Guid.NewGuid():N}"; @@ -2950,7 +2950,7 @@ public async Task LogoutAllSelf_ShouldInvalidateAllDeviceSessions() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device1 = $"logout-all-device-1-{Guid.NewGuid():N}"; var device2 = $"logout-all-device-2-{Guid.NewGuid():N}"; @@ -3006,7 +3006,7 @@ public async Task LogoutAllSelf_ShouldInvalidateCallingSession() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device = $"logout-all-current-{Guid.NewGuid():N}"; @@ -3070,7 +3070,7 @@ public async Task LogoutAllSelf_ShouldDetachSessionsWithoutRevokingChains() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device1 = $"logout-all-chain-1-{Guid.NewGuid():N}"; @@ -3112,7 +3112,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var before = await beforeResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); before.Should().NotBeNull(); @@ -3171,7 +3171,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var after = await afterResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); after.Should().NotBeNull(); @@ -3210,8 +3210,8 @@ public async Task LogoutAllSelf_ShouldNotAffectAnotherUsersSessions() { _factory.Clock.Reset(); - var actor = await _factory.CreateLoginUserAsync(); - var victim = await _factory.CreateLoginUserAsync(); + var actor = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var victim = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var actorClient1 = CreateClient( $"logout-all-actor-1-{Guid.NewGuid():N}"); @@ -3302,7 +3302,7 @@ public async Task LogoutAllSelf_LoggedOutDevices_ShouldBeAbleToLoginAgain() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var device1 = $"logout-all-relogin-1-{Guid.NewGuid():N}"; @@ -3337,7 +3337,7 @@ await LoginAsync( await GetChainsAsync(client1); var before = await beforeResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); before.Should().NotBeNull(); @@ -3421,7 +3421,7 @@ public async Task ConcurrentLogoutAllSelf_ShouldConvergeToAllSessionsInvalid() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var actorDevice = $"logout-all-concurrent-actor-{Guid.NewGuid():N}"; @@ -3470,7 +3470,7 @@ await LoginAsync( if (response.StatusCode == HttpStatusCode.BadRequest) { var body = await response.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(TestContext.Current.CancellationToken); body.Should().NotBeNull(); body.Should().ContainKey("error") diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/PkceFlowIntegrationTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/PkceFlowIntegrationTests.cs index 3ba88eeb..a73d90ef 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/PkceFlowIntegrationTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/PkceFlowIntegrationTests.cs @@ -31,7 +31,7 @@ public async Task Pkce_WithValidVerifierAndCredentials_ShouldAuthenticate() _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( @@ -76,7 +76,7 @@ public async Task Pkce_AfterSuccessfulCompletion_ShouldRejectReplayOfAuthorizati _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( @@ -127,7 +127,7 @@ public async Task Pkce_WithInvalidVerifier_ShouldConsumeAuthorizationCode() _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( @@ -181,7 +181,7 @@ public async Task Pkce_AfterAuthorizationCodeExpires_ShouldRejectCompletion() _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( @@ -225,7 +225,7 @@ public async Task Pkce_WithValidProofButInvalidCredentials_ShouldNotAuthenticate _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( @@ -260,7 +260,7 @@ public async Task Pkce_WithInvalidProof_ShouldNotAuthenticateEvenWithValidCreden _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( @@ -379,7 +379,7 @@ public async Task Pkce_WithValidProofAndRepeatedInvalidCredentials_ShouldReachCr _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/RateLimitingFlowTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/RateLimitingFlowTests.cs index 3463c6a5..b9d185a4 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/RateLimitingFlowTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/RateLimitingFlowTests.cs @@ -22,7 +22,7 @@ public async Task RejectedLoginDoesNotLockAccount_AndRejectedLogoutDoesNotRevoke o.RateLimiting.Endpoints[UAuthEndpoint.Logout] = new() { PermitLimit = 1 }; o.Login.MaxFailedAttempts = 1; }); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); HttpClient Client(string device) { var client = factory.CreateClient(new WebApplicationFactoryClientOptions @@ -45,10 +45,10 @@ HttpClient Client(string device) using (var scope = factory.Services.CreateScope()) { var security = scope.ServiceProvider.GetRequiredService(); - var factor = await security.GetOrCreateFactorAsync(TenantKey.Single, user.UserKey, CredentialType.Password); + var factor = await security.GetOrCreateFactorAsync(TenantKey.Single, user.UserKey, CredentialType.Password, TestContext.Current.CancellationToken); Assert.Equal(0, factor.FailedAttempts); } - Assert.NotEqual(HttpStatusCode.TooManyRequests, (await first.PostAsync("/auth/logout", null)).StatusCode); + Assert.NotEqual(HttpStatusCode.TooManyRequests, (await first.PostAsync("/auth/logout", null, TestContext.Current.CancellationToken)).StatusCode); // Two validated sessions of the same actor share logout capacity. var logout = await CsrfTestClient.PostAsync(second, "/auth/logout", null); Assert.Equal(HttpStatusCode.TooManyRequests, logout.StatusCode); @@ -56,7 +56,7 @@ HttpClient Client(string device) // The rejected logout did not revoke the second session. var validation = await CsrfTestClient.PostAsync(second, "/auth/validate", null); Assert.Equal(HttpStatusCode.OK, validation.StatusCode); - var result = await validation.Content.ReadFromJsonAsync(); + var result = await validation.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); Assert.True(result?.IsValid); Assert.NotNull(result!.Snapshot); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/RateLimitingProofFlowTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/RateLimitingProofFlowTests.cs index 0e81c547..0ba5a65a 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/RateLimitingProofFlowTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/RateLimitingProofFlowTests.cs @@ -26,14 +26,14 @@ public async Task PkceCompletion_RejectedRequestPreservesAuthorizationProof() { using var factory = AuthServerFactory.Create(o => { o.RateLimiting.Enabled = true; o.RateLimiting.Login = new() { PermitLimit = 1, Window = Window }; }); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = Client(factory); static string Encode(byte[] value) => Convert.ToBase64String(value).TrimEnd('=').Replace('+', '-').Replace('/', '_'); var verifier = Encode(RandomNumberGenerator.GetBytes(32)); var authorize = await CsrfTestClient.PostAsync(client, "/auth/pkce/authorize", new FormUrlEncodedContent(new Dictionary { ["code_challenge"] = Encode(SHA256.HashData(Encoding.ASCII.GetBytes(verifier))), ["challenge_method"] = "S256", ["redirect_uri"] = "/callback" })); Assert.Equal(HttpStatusCode.OK, authorize.StatusCode); - var proof = await authorize.Content.ReadFromJsonAsync(); + var proof = await authorize.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); Assert.NotNull(proof); // Malformed payload consumes request quota without supplying the valid authorization proof. Assert.Equal(HttpStatusCode.BadRequest, (await CsrfTestClient.PostAsync(client, "/auth/pkce/complete", new FormUrlEncodedContent(new Dictionary()))).StatusCode); @@ -58,7 +58,7 @@ public async Task Refresh_RejectedRequestPreservesRefreshToken_ThenSameTokenRota { using var factory = AuthServerFactory.Create(o => { o.RateLimiting.Enabled = true; o.RateLimiting.Endpoints[UAuthEndpoint.Refresh] = new() { PermitLimit = 1, Window = Window }; }); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = Client(factory); client.DefaultRequestHeaders.Add("X-UAuth-ClientProfile", "BlazorWasm"); var login = await CsrfTestClient.PostJsonAsync(client, "/auth/login", new { identifier = user.Identifier, secret = user.Secret }); @@ -68,7 +68,7 @@ public async Task Refresh_RejectedRequestPreservesRefreshToken_ThenSameTokenRota using var scope = factory.Services.CreateScope(); var store = scope.ServiceProvider.GetRequiredService().Create(TenantKey.Single); var hash = scope.ServiceProvider.GetRequiredService().Hash(refresh); - var before = await store.FindByHashAsync(hash); + var before = await store.FindByHashAsync(hash, TestContext.Current.CancellationToken); Assert.NotNull(before); client.DefaultRequestHeaders.Add("Cookie", session); Assert.Equal(HttpStatusCode.Unauthorized, (await CsrfTestClient.PostAsync(client, "/auth/refresh", null)).StatusCode); @@ -77,11 +77,11 @@ public async Task Refresh_RejectedRequestPreservesRefreshToken_ThenSameTokenRota var rejected = await CsrfTestClient.PostAsync(client, "/auth/refresh", null); Assert.Equal(HttpStatusCode.TooManyRequests, rejected.StatusCode); Assert.False(rejected.Headers.Contains("Set-Cookie")); - Assert.Equal(before, await store.FindByHashAsync(hash)); + Assert.Equal(before, await store.FindByHashAsync(hash, TestContext.Current.CancellationToken)); var renewed = await AfterReplenishment(() => CsrfTestClient.PostAsync(client, "/auth/refresh", null)); Assert.Equal(HttpStatusCode.NoContent, renewed.StatusCode); Assert.True(renewed.Headers.Contains("Set-Cookie")); - Assert.False((await store.FindByHashAsync(hash))!.IsActive(factory.Clock.UtcNow)); + Assert.False((await store.FindByHashAsync(hash, TestContext.Current.CancellationToken))!.IsActive(factory.Clock.UtcNow)); } [Fact] @@ -91,7 +91,7 @@ public async Task Recovery_RejectedRequestPreservesResetProof_ThenSameProofChang using var factory = AuthServerFactory.Create(o => { o.RateLimiting.Enabled = true; o.RateLimiting.Endpoints[UAuthEndpoint.CredentialResetCompleteAnonymous] = new() { PermitLimit = 1, Window = Window }; }, services => { services.RemoveAll(); services.AddSingleton(notifier); }); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = Client(factory); var begin = await CsrfTestClient.PostJsonAsync(client, "/auth/me/credentials/reset/begin", new BeginResetCredentialRequest { Identifier = user.Identifier, CredentialType = CredentialType.Password, ResetCodeType = ResetCodeType.Token }); @@ -118,12 +118,12 @@ public async Task AuthenticatedUsersOnSameConnectionIpHaveIndependentValidationB using var second = Client(factory); foreach (var client in new[] { first, second }) { - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var login = await CsrfTestClient.PostJsonAsync(client, "/auth/login", new { identifier = user.Identifier, secret = user.Secret }); client.DefaultRequestHeaders.Add("Cookie", Cookies(login)); var validation = await CsrfTestClient.PostAsync(client, "/auth/validate", null); Assert.Equal(HttpStatusCode.OK, validation.StatusCode); - Assert.True((await validation.Content.ReadFromJsonAsync())!.IsValid); + Assert.True((await validation.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken))!.IsValid); Assert.Equal(HttpStatusCode.TooManyRequests, (await CsrfTestClient.PostAsync(client, "/auth/validate", null)).StatusCode); } } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/RateLimitingTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/RateLimitingTests.cs index da4ffeef..7fa8f5e0 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/RateLimitingTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/RateLimitingTests.cs @@ -28,7 +28,7 @@ public async Task LoginAliasesShareBudget_AndMaintenanceIsIndependent() if (response.Headers.RetryAfter is not null) Assert.True(response.Headers.RetryAfter.Delta > TimeSpan.Zero); Assert.True(response.Headers.CacheControl?.NoStore); - Assert.Contains("rate_limited", await response.Content.ReadAsStringAsync()); + Assert.Contains("rate_limited", await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)); } foreach (var route in new[] { "/auth/validate", "/auth/refresh", "/auth/logout" }) Assert.Equal(HttpStatusCode.NoContent, (await host.Post(route)).StatusCode); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/RefreshTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/RefreshTests.cs index 8725fb56..47e9b25a 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/RefreshTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/RefreshTests.cs @@ -40,7 +40,7 @@ public async Task Refresh_PureOpaque_BeforeTouchInterval_ShouldNotMutateChain() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await LoginAsync(user, "BlazorServer"); @@ -74,7 +74,7 @@ public async Task Refresh_PureOpaque_AfterTouchInterval_ShouldTouchChain() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await LoginAsync(user, "BlazorServer"); @@ -114,7 +114,7 @@ public async Task Refresh_PureOpaque_AfterTouch_ShouldNotTouchAgainBeforeNextInt { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await LoginAsync(user, "BlazorServer"); @@ -159,7 +159,7 @@ public async Task Refresh_PureOpaque_ShouldPreserveSessionAndChainIdentity() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await LoginAsync(user, "BlazorServer"); @@ -208,7 +208,7 @@ public async Task Refresh_PureOpaque_WithRevokedSession_ShouldReturnUnauthorized _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await LoginAsync( user, @@ -230,10 +230,10 @@ await store.ExecuteAsync( ct => store.RevokeSessionAsync( sessionId, _factory.Clock.UtcNow, - ct)); + ct), TestContext.Current.CancellationToken); var persisted = - await store.GetSessionAsync(sessionId); + await store.GetSessionAsync(sessionId, TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.IsRevoked.Should().BeTrue(); @@ -251,7 +251,7 @@ public async Task Refresh_PureOpaque_WithRevokedChain_ShouldReturnUnauthorized() _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await LoginAsync( user, @@ -267,11 +267,11 @@ await store.ExecuteAsync( ct => store.RevokeChainCascadeAsync( current.ChainId, _factory.Clock.UtcNow, - ct)); + ct), TestContext.Current.CancellationToken); var persistedChain = await store.GetChainAsync( - current.ChainId); + current.ChainId, TestContext.Current.CancellationToken); persistedChain.Should().NotBeNull(); persistedChain!.IsRevoked.Should().BeTrue(); @@ -289,7 +289,7 @@ public async Task Refresh_PureOpaque_WithRevokedRoot_ShouldReturnUnauthorized() _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await LoginAsync( user, @@ -303,7 +303,7 @@ await LoginAsync( var rootBefore = await store.GetActiveRootByUserAsync( - user.UserKey); + user.UserKey, TestContext.Current.CancellationToken); rootBefore.Should().NotBeNull(); rootBefore!.IsRevoked.Should().BeFalse(); @@ -312,14 +312,14 @@ await store.ExecuteAsync( ct => store.RevokeRootCascadeAsync( user.UserKey, _factory.Clock.UtcNow, - ct)); + ct), TestContext.Current.CancellationToken); // // Active lookup must no longer return the revoked root. // var activeRootAfter = await store.GetActiveRootByUserAsync( - user.UserKey); + user.UserKey, TestContext.Current.CancellationToken); activeRootAfter.Should().BeNull(); @@ -328,7 +328,7 @@ await store.GetActiveRootByUserAsync( // var historicalRoot = await store.GetRootByIdAsync( - rootBefore.RootId); + rootBefore.RootId, TestContext.Current.CancellationToken); historicalRoot.Should().NotBeNull(); historicalRoot!.IsRevoked.Should().BeTrue(); @@ -338,7 +338,7 @@ await store.GetRootByIdAsync( // var chainAfter = await store.GetChainAsync( - current.ChainId); + current.ChainId, TestContext.Current.CancellationToken); chainAfter.Should().NotBeNull(); chainAfter!.IsRevoked.Should().BeTrue(); @@ -356,7 +356,7 @@ public async Task Refresh_PureOpaque_FromDifferentDevice_ShouldReturnUnauthorize _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var cookie = await LoginAsync( @@ -427,7 +427,7 @@ public async Task Refresh_Hybrid_ShouldRotateCredentials() _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var beforeCookie = await LoginAsync( @@ -474,7 +474,7 @@ public async Task Refresh_Hybrid_ShouldPreserveChainIdentity() _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await LoginAsync( user, @@ -507,7 +507,7 @@ public async Task Refresh_Hybrid_RotatedCredential_ShouldBeUsableForNextRefresh( _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await LoginAsync( user, @@ -548,7 +548,7 @@ public async Task Refresh_Hybrid_ReusingPreviousCredential_ShouldReturnUnauthori _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var originalCookie = await LoginAsync( @@ -593,7 +593,7 @@ public async Task Refresh_Hybrid_WithoutRefreshToken_ShouldReturnUnauthorized() _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var cookie = await LoginAsync( @@ -622,7 +622,7 @@ public async Task Refresh_Hybrid_WithRefreshTokenFromDifferentSession_ShouldRetu _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var clientA = CreateClient( @@ -674,7 +674,7 @@ public async Task Refresh_Hybrid_FromDifferentDevice_ShouldReturnUnauthorized() _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var originalClient = CreateClient( @@ -723,7 +723,7 @@ public async Task Refresh_Hybrid_WithRevokedSession_ShouldReturnUnauthorized() _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await LoginAsync( user, @@ -751,7 +751,7 @@ public async Task Refresh_Hybrid_WithRevokedChain_ShouldReturnUnauthorized() _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await LoginAsync( user, @@ -776,7 +776,7 @@ public async Task Refresh_Hybrid_WithRevokedRoot_ShouldReturnUnauthorized() _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await LoginAsync( user, @@ -798,7 +798,7 @@ public async Task Refresh_Hybrid_ConcurrentReuseOfSameRefreshToken_ShouldAllowEx _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"hybrid-race-{Guid.NewGuid():N}"; @@ -856,7 +856,7 @@ public async Task Refresh_Hybrid_ConcurrentDuplicate_ShouldLeaveWinningCredentia factory.Clock.Reset(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"hybrid-concurrent-{Guid.NewGuid():N}"; @@ -934,7 +934,7 @@ public async Task Refresh_Hybrid_AfterConcurrentRotation_OriginalRefreshToken_Sh _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"hybrid-race-replay-{Guid.NewGuid():N}"; @@ -1004,7 +1004,7 @@ public async Task Refresh_Hybrid_ConcurrentRotation_ShouldNotCreateAnotherChain( _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"hybrid-race-chain-{Guid.NewGuid():N}"; @@ -1083,7 +1083,7 @@ public async Task Refresh_Hybrid_AfterSessionRevoked_ShouldReturnUnauthorized() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"refresh-revoke-session-{Guid.NewGuid():N}"; using var client = CreateClient(deviceId); @@ -1114,7 +1114,7 @@ await store.ExecuteAsync( ct => store.RevokeSessionAsync( before.ActiveSessionId!.Value, _factory.Clock.UtcNow, - ct)); + ct), TestContext.Current.CancellationToken); } client.DefaultRequestHeaders.Remove("Cookie"); @@ -1132,7 +1132,7 @@ public async Task Refresh_Hybrid_AfterChainRevoked_ShouldReturnUnauthorized() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"refresh-revoke-chain-{Guid.NewGuid():N}"; using var client = CreateClient(deviceId); @@ -1157,7 +1157,7 @@ await store.ExecuteAsync( ct => store.RevokeChainCascadeAsync( before.ChainId, _factory.Clock.UtcNow, - ct)); + ct), TestContext.Current.CancellationToken); } client.DefaultRequestHeaders.Remove("Cookie"); @@ -1174,7 +1174,7 @@ public async Task Refresh_Hybrid_AfterRootRevoked_ShouldReturnUnauthorized() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"refresh-revoke-root-{Guid.NewGuid():N}"; using var client = CreateClient(deviceId); @@ -1201,7 +1201,7 @@ await store.ExecuteAsync( ct => store.RevokeRootCascadeAsync( user.UserKey, _factory.Clock.UtcNow, - ct)); + ct), TestContext.Current.CancellationToken); } client.DefaultRequestHeaders.Remove("Cookie"); @@ -1218,7 +1218,7 @@ public async Task Refresh_Hybrid_ReusingRotatedToken_ShouldReturnUnauthorized() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"refresh-replay-{Guid.NewGuid():N}"; using var loginClient = CreateClient(deviceId); @@ -1271,7 +1271,7 @@ public async Task Refresh_Hybrid_ReplayAfterConcurrencyWindow_ShouldInvalidateRe factory.Clock.Reset(); var user = - await factory.CreateLoginUserAsync(); + await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"refresh-replay-family-{Guid.NewGuid():N}"; @@ -1357,7 +1357,7 @@ public async Task Refresh_Hybrid_DuplicateWithinConcurrencyWindow_ShouldNotInval factory.Clock.Reset(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"refresh-duplicate-window-{Guid.NewGuid():N}"; @@ -1403,7 +1403,7 @@ public async Task Refresh_Hybrid_ReplayOnOneChain_ShouldNotAffectOtherDeviceChai _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceA = $"refresh-compromised-{Guid.NewGuid():N}"; @@ -1495,7 +1495,7 @@ public async Task Refresh_Hybrid_JustBeforeRefreshTokenExpiry_ShouldSucceed() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"hybrid-expiry-before-{Guid.NewGuid():N}"; using var client = CreateClient(deviceId); @@ -1524,7 +1524,7 @@ public async Task Refresh_Hybrid_AtRefreshTokenExpiry_ShouldReturnUnauthorized() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"hybrid-expiry-exact-{Guid.NewGuid():N}"; using var client = CreateClient(deviceId); @@ -1553,7 +1553,7 @@ public async Task Refresh_Hybrid_AfterRefreshTokenExpiry_ShouldReturnUnauthorize { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"hybrid-expiry-after-{Guid.NewGuid():N}"; using var client = CreateClient(deviceId); @@ -1590,7 +1590,7 @@ public async Task Refresh_Hybrid_WhenSessionExpired_ShouldReturnUnauthorized() factory.Clock.Reset(); var user = - await factory.CreateLoginUserAsync(); + await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"hybrid-session-expiry-{Guid.NewGuid():N}"; @@ -1636,7 +1636,7 @@ public async Task Refresh_Hybrid_WhenChainIdleTimeoutExceeded_ShouldReturnUnauth factory.Clock.Reset(); var user = - await factory.CreateLoginUserAsync(); + await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"hybrid-idle-expiry-{Guid.NewGuid():N}"; @@ -1681,7 +1681,7 @@ public async Task Refresh_Hybrid_JustBeforeChainIdleTimeout_ShouldSucceed() factory.Clock.Reset(); var user = - await factory.CreateLoginUserAsync(); + await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"hybrid-idle-before-{Guid.NewGuid():N}"; @@ -1727,7 +1727,7 @@ public async Task Refresh_Hybrid_AtChainIdleTimeout_ShouldReturnUnauthorized() factory.Clock.Reset(); var user = - await factory.CreateLoginUserAsync(); + await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"hybrid-idle-exact-{Guid.NewGuid():N}"; @@ -1768,7 +1768,7 @@ public async Task Refresh_Hybrid_OnSuccess_ShouldRotateRefreshCookie() _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"hybrid-cookie-rotate-{Guid.NewGuid():N}"; @@ -1812,7 +1812,7 @@ public async Task Refresh_Hybrid_OnSuccess_ShouldNotRotateSessionCookie() _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"hybrid-cookie-session-{Guid.NewGuid():N}"; @@ -1857,7 +1857,7 @@ public async Task Refresh_Hybrid_OnUnauthorized_ShouldNotIssueReplacementRefresh _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"hybrid-cookie-failure-{Guid.NewGuid():N}"; @@ -1909,7 +1909,7 @@ public async Task Refresh_Hybrid_OnReplayDetection_ShouldNotIssueReplacementRefr _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"hybrid-cookie-replay-{Guid.NewGuid():N}"; @@ -1972,7 +1972,7 @@ public async Task Refresh_Hybrid_ConcurrentLoser_ShouldNotReceiveReplacementRefr _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var deviceId = $"hybrid-cookie-race-{Guid.NewGuid():N}"; @@ -2063,7 +2063,7 @@ public async Task Refresh_PureOpaque_OnSuccess_ShouldNotIssueRefreshCookie() _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await LoginAsync( user, @@ -2095,7 +2095,7 @@ public async Task Refresh_PureOpaque_OnSuccess_ShouldPreserveSessionCookie() _factory.Clock.Reset(); var user = - await _factory.CreateLoginUserAsync(); + await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var originalCookies = await LoginAsync( @@ -2148,7 +2148,7 @@ public async Task Refresh_Hybrid_CredentialFromTenantA_ShouldNotBeUsableInTenant var userA = await factory.CreateLoginUserAsync( - tenant: tenantA); + tenant: tenantA, ct: TestContext.Current.CancellationToken); using var loginClient = CreateClient( @@ -2210,7 +2210,7 @@ public async Task Refresh_Hybrid_CrossTenantAttempt_ShouldNotInvalidateSourceTen var userA = await factory.CreateLoginUserAsync( - tenant: tenantA); + tenant: tenantA, ct: TestContext.Current.CancellationToken); var deviceId = $"tenant-isolation-{Guid.NewGuid():N}"; @@ -2306,11 +2306,11 @@ public async Task Refresh_Hybrid_DifferentTenants_ShouldRotateIndependently() var userA = await factory.CreateLoginUserAsync( - tenant: tenantA); + tenant: tenantA, ct: TestContext.Current.CancellationToken); var userB = await factory.CreateLoginUserAsync( - tenant: tenantB); + tenant: tenantB, ct: TestContext.Current.CancellationToken); using var clientA = CreateClient( @@ -2379,11 +2379,11 @@ public async Task Refresh_Hybrid_SessionFromTenantAAndRefreshFromTenantB_ShouldB var userA = await factory.CreateLoginUserAsync( - tenant: tenantA); + tenant: tenantA, ct: TestContext.Current.CancellationToken); var userB = await factory.CreateLoginUserAsync( - tenant: tenantB); + tenant: tenantB, ct: TestContext.Current.CancellationToken); using var clientA = CreateClient(factory, deviceId); @@ -2444,7 +2444,7 @@ public async Task Refresh_Hybrid_Maui_HeaderCredentials_ShouldSupportConsecutive { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"maui-refresh-{Guid.NewGuid():N}"); @@ -2488,7 +2488,7 @@ public async Task Refresh_Hybrid_Maui_HeaderCredentials_ShouldSupportConsecutive first.StatusCode.Should().Be( HttpStatusCode.NoContent, - $"first refresh failed: {await first.Content.ReadAsStringAsync()}"); + $"first refresh failed: {await first.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)}"); var rotatedRefreshToken = ReadCredential(first, "uar"); @@ -2517,7 +2517,7 @@ public async Task Refresh_Hybrid_Maui_HeaderCredentials_ShouldSupportConsecutive second.StatusCode.Should().Be( HttpStatusCode.NoContent, - $"second refresh failed: {await second.Content.ReadAsStringAsync()}"); + $"second refresh failed: {await second.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)}"); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/ResourceApi/ResourceApiAuthenticationTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/ResourceApi/ResourceApiAuthenticationTests.cs index 9d4c94b3..3a34937f 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/ResourceApi/ResourceApiAuthenticationTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/ResourceApi/ResourceApiAuthenticationTests.cs @@ -36,7 +36,7 @@ await ResourceApiTestHost.CreateAsync( validator.Object); var response = await host.Client.GetAsync( - "/__tests/resource/anonymous"); + "/__tests/resource/anonymous", TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.OK); @@ -61,7 +61,7 @@ await ResourceApiTestHost.CreateAsync( validator.Object); var response = await host.Client.GetAsync( - "/__tests/resource/authenticated"); + "/__tests/resource/authenticated", TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.Unauthorized); @@ -97,7 +97,7 @@ await ResourceApiTestHost.CreateAsync( AddSession(host.Client); var response = await host.Client.GetAsync( - "/__tests/resource/authenticated"); + "/__tests/resource/authenticated", TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.Unauthorized); @@ -126,7 +126,7 @@ await ResourceApiTestHost.CreateAsync( AddSession(host.Client); var response = await host.Client.GetAsync( - "/__tests/resource/authenticated"); + "/__tests/resource/authenticated", TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.OK); @@ -150,7 +150,7 @@ await ResourceApiTestHost.CreateAsync( AddSession(host.Client); var response = await host.Client.GetAsync( - "/__tests/resource/admin"); + "/__tests/resource/admin", TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.Forbidden); @@ -174,7 +174,7 @@ await ResourceApiTestHost.CreateAsync( AddSession(host.Client); var response = await host.Client.GetAsync( - "/__tests/resource/admin"); + "/__tests/resource/admin", TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.OK); @@ -200,14 +200,14 @@ await ResourceApiTestHost.CreateAsync( AddSession(host.Client); var response = await host.Client.GetAsync( - "/__tests/resource/identity"); + "/__tests/resource/identity", TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.OK); var body = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); body.Should().NotBeNull(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/ResourceApi/ResourceApiAuthorizationTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/ResourceApi/ResourceApiAuthorizationTests.cs index afc84bd6..013999c3 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/ResourceApi/ResourceApiAuthorizationTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/ResourceApi/ResourceApiAuthorizationTests.cs @@ -50,7 +50,7 @@ public async Task PolicyEndpoint_WithoutCredential_ShouldReturnUnauthorized() await ResourceApiTestHost.CreateAsync(validator.Object); var response = await host.Client.GetAsync( - "/__tests/resource/products/read"); + "/__tests/resource/products/read", TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.Unauthorized); @@ -75,7 +75,7 @@ public async Task PolicyEndpoint_WithInvalidSession_ShouldReturnUnauthorized() AddSession(host.Client); var response = await host.Client.GetAsync( - "/__tests/resource/products/read"); + "/__tests/resource/products/read", TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.Unauthorized); @@ -99,7 +99,7 @@ await CreateHostAsync( AddSession(host.Client); var response = await host.Client.GetAsync( - "/__tests/resource/products/read"); + "/__tests/resource/products/read", TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.OK); @@ -129,7 +129,7 @@ await CreateHostAsync( AddSession(host.Client); var response = await host.Client.GetAsync( - "/__tests/resource/products/read"); + "/__tests/resource/products/read", TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.Forbidden); @@ -166,7 +166,7 @@ await CreateHostAsync( AddSession(host.Client); var response = await host.Client.GetAsync( - "/__tests/resource/products/read"); + "/__tests/resource/products/read", TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.OK); @@ -224,7 +224,7 @@ await CreateHostAsync( AddSession(host.Client); var response = await host.Client.GetAsync( - "/__tests/resource/products/read"); + "/__tests/resource/products/read", TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.OK); @@ -267,7 +267,7 @@ await CreateHostAsync( AddSession(host.Client); var response = await host.Client.GetAsync( - "/__tests/resource/products/read"); + "/__tests/resource/products/read", TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.OK); @@ -316,7 +316,7 @@ await CreateHostAsync( HttpMethod.Put, "/__tests/resource/products/42"); - var response = await host.Client.SendAsync(request); + var response = await host.Client.SendAsync(request, TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.OK); @@ -356,7 +356,7 @@ public async Task PolicyEndpoint_WhenReauthenticationRequired_ShouldReturn401Pro AddSession(host.Client); using var response = await host.Client.GetAsync( - "/__tests/resource/products/read"); + "/__tests/resource/products/read", TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); @@ -364,7 +364,7 @@ public async Task PolicyEndpoint_WhenReauthenticationRequired_ShouldReturn401Pro .MediaType.Should().Be("application/problem+json"); var problem = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); problem.Should().NotBeNull(); problem!.Status.Should().Be(401); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/ResourceApi/ResourceApiTenantIsolationTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/ResourceApi/ResourceApiTenantIsolationTests.cs index bd199cd8..0610401b 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/ResourceApi/ResourceApiTenantIsolationTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/ResourceApi/ResourceApiTenantIsolationTests.cs @@ -61,7 +61,7 @@ public async Task RequestTenant_ShouldBePassedToSessionValidation() AddTenant(host.Client, "tenant-a"); var response = await host.Client.GetAsync( - "/__tests/resource/authenticated"); + "/__tests/resource/authenticated", TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.OK); @@ -91,7 +91,7 @@ public async Task TenantA_WithTenantASession_ShouldAuthenticate() AddTenant(host.Client, "tenant-a"); var response = await host.Client.GetAsync( - "/__tests/resource/authenticated"); + "/__tests/resource/authenticated", TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.OK); @@ -118,7 +118,7 @@ public async Task TenantB_WithTenantASession_ShouldNotAuthenticate() AddTenant(host.Client, "tenant-b"); var response = await host.Client.GetAsync( - "/__tests/resource/authenticated"); + "/__tests/resource/authenticated", TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.Unauthorized); @@ -156,7 +156,7 @@ public async Task SameSessionId_ShouldRemainTenantScoped() AddTenant(host.Client, "tenant-a"); var tenantAResponse = await host.Client.GetAsync( - "/__tests/resource/authenticated"); + "/__tests/resource/authenticated", TestContext.Current.CancellationToken); tenantAResponse.StatusCode.Should() .Be(HttpStatusCode.OK); @@ -165,7 +165,7 @@ public async Task SameSessionId_ShouldRemainTenantScoped() AddTenant(host.Client, "tenant-b"); var tenantBResponse = await host.Client.GetAsync( - "/__tests/resource/authenticated"); + "/__tests/resource/authenticated", TestContext.Current.CancellationToken); tenantBResponse.StatusCode.Should() .Be(HttpStatusCode.Unauthorized); @@ -199,14 +199,14 @@ public async Task ResolvedTenant_ShouldBePreservedInValidationContext() AddTenant(host.Client, "tenant-a"); var tenantAResponse = await host.Client.GetAsync( - "/__tests/resource/authenticated"); + "/__tests/resource/authenticated", TestContext.Current.CancellationToken); host.Client.DefaultRequestHeaders.Remove(TenantHeader); AddTenant(host.Client, "tenant-b"); var tenantBResponse = await host.Client.GetAsync( - "/__tests/resource/authenticated"); + "/__tests/resource/authenticated", TestContext.Current.CancellationToken); tenantAResponse.StatusCode.Should() .Be(HttpStatusCode.OK); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/SdkEnpointContractTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/SdkEnpointContractTests.cs index 5f4d16da..b0200497 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/SdkEnpointContractTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/SdkEnpointContractTests.cs @@ -24,7 +24,7 @@ public sealed class SdkEndpointContractTests public async Task ChangeMyStatus_ShouldReturnTypedJson_NotEmpty200() { using var factory = new AuthServerFactory(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient(factory); await AuthenticateAsync(client, user); using var response = await CsrfTestClient.PostJsonAsync(client, "/auth/me/status", @@ -39,9 +39,9 @@ public async Task ChangeMyStatus_ShouldReturnTypedJson_NotEmpty200() public async Task ChangeUserStatus_ShouldReturnTypedJson_NotEmpty200() { using var factory = new AuthServerFactory(); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); - await factory.GrantPermissionsAsync(admin.UserKey, [UAuthActions.Users.ChangeStatusAdmin]); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + await factory.GrantPermissionsAsync(admin.UserKey, [UAuthActions.Users.ChangeStatusAdmin], TestContext.Current.CancellationToken); using var client = CreateClient(factory); await AuthenticateAsync(client, admin); using var response = await CsrfTestClient.PostJsonAsync( @@ -58,9 +58,9 @@ public async Task ChangeUserStatus_ShouldReturnTypedJson_NotEmpty200() public async Task ChangeUserStatus_AlreadyInRequestedStatus_ShouldReturnTypedJson() { using var factory = new AuthServerFactory(); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); - await factory.GrantPermissionsAsync(admin.UserKey, [UAuthActions.Users.ChangeStatusAdmin]); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + await factory.GrantPermissionsAsync(admin.UserKey, [UAuthActions.Users.ChangeStatusAdmin], TestContext.Current.CancellationToken); using var client = CreateClient(factory); await AuthenticateAsync(client, admin); var url = $"/auth/admin/users/{target.UserKey.Value}/status"; @@ -78,9 +78,9 @@ public async Task ChangeUserStatus_AlreadyInRequestedStatus_ShouldReturnTypedJso public async Task DeleteUser_ShouldReturnTypedJson_WithRequestedMode() { using var factory = new AuthServerFactory(); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); - await factory.GrantPermissionsAsync(admin.UserKey, [UAuthActions.Users.DeleteAdmin]); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + await factory.GrantPermissionsAsync(admin.UserKey, [UAuthActions.Users.DeleteAdmin], TestContext.Current.CancellationToken); using var client = CreateClient(factory); await AuthenticateAsync(client, admin); using var response = await CsrfTestClient.PostJsonAsync( @@ -97,9 +97,9 @@ public async Task AdminBeginReset_ShouldReturnTypedJson_WithoutProof() { var notifier = new CapturingResetNotifier(); using var factory = CreateResetFactory(notifier); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); - await factory.GrantPermissionsAsync(admin.UserKey, [UAuthActions.Credentials.BeginResetAdmin]); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + await factory.GrantPermissionsAsync(admin.UserKey, [UAuthActions.Credentials.BeginResetAdmin], TestContext.Current.CancellationToken); using var client = CreateClient(factory); await AuthenticateAsync(client, admin); using var response = await CsrfTestClient.PostJsonAsync( @@ -107,7 +107,7 @@ public async Task AdminBeginReset_ShouldReturnTypedJson_WithoutProof() $"/auth/admin/users/{target.UserKey.Value}/credentials/reset/begin", BeginResetRequest(target)); var result = await AssertTypedSuccessAsync(response); result.ExpiresAt.Should().BeAfter(factory.Clock.UtcNow); - var body = await response.Content.ReadAsStringAsync(); + var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); var notification = notifier.Notifications.Should().ContainSingle(x => x.UserKey == target.UserKey).Subject; body.Should().NotContain(notification.Token, "reset proof must only be sent through the notifier"); } @@ -117,10 +117,10 @@ public async Task AdminCompleteReset_ShouldReturnTypedJson_AndChangeCredential() { var notifier = new CapturingResetNotifier(); using var factory = CreateResetFactory(notifier); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await factory.GrantPermissionsAsync(admin.UserKey, - [UAuthActions.Credentials.BeginResetAdmin, UAuthActions.Credentials.CompleteResetAdmin]); + [UAuthActions.Credentials.BeginResetAdmin, UAuthActions.Credentials.CompleteResetAdmin], TestContext.Current.CancellationToken); using var client = CreateClient(factory); await AuthenticateAsync(client, admin); var url = $"/auth/admin/users/{target.UserKey.Value}/credentials/reset"; @@ -145,14 +145,14 @@ public async Task AdminBeginReset_TargetMismatch_ShouldReturn400_AndNotNotify() { var notifier = new CapturingResetNotifier(); using var factory = CreateResetFactory(notifier); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); - var other = await factory.CreateLoginUserAsync(); - await factory.GrantPermissionsAsync(admin.UserKey, [UAuthActions.Credentials.BeginResetAdmin]); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var other = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + await factory.GrantPermissionsAsync(admin.UserKey, [UAuthActions.Credentials.BeginResetAdmin], TestContext.Current.CancellationToken); using var client = CreateClient(factory); await AuthenticateAsync(client, admin); using var response = await client.PostAsJsonAsync( - $"/auth/admin/users/{target.UserKey.Value}/credentials/reset/begin", BeginResetRequest(other)); + $"/auth/admin/users/{target.UserKey.Value}/credentials/reset/begin", BeginResetRequest(other), cancellationToken: TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); notifier.Notifications.Should().BeEmpty(); } @@ -161,7 +161,7 @@ public async Task AdminBeginReset_TargetMismatch_ShouldReturn400_AndNotNotify() public async Task RevokeMy_UnknownCredential_ShouldNotReturnSuccessful204() { using var factory = new AuthServerFactory(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient(factory); await AuthenticateAsync(client, user); using var response = await CsrfTestClient.PostJsonAsync(client, "/auth/me/credentials/revoke", @@ -173,9 +173,9 @@ public async Task RevokeMy_UnknownCredential_ShouldNotReturnSuccessful204() public async Task RevokeAdmin_UnknownCredential_ShouldNotReturnSuccessful204() { using var factory = new AuthServerFactory(); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); - await factory.GrantPermissionsAsync(admin.UserKey, [UAuthActions.Credentials.RevokeAdmin]); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + await factory.GrantPermissionsAsync(admin.UserKey, [UAuthActions.Credentials.RevokeAdmin], TestContext.Current.CancellationToken); using var client = CreateClient(factory); await AuthenticateAsync(client, admin); using var response = await CsrfTestClient.PostJsonAsync(client, @@ -188,9 +188,9 @@ public async Task RevokeAdmin_UnknownCredential_ShouldNotReturnSuccessful204() public async Task DeleteCredentialAdmin_UnknownCredential_ShouldNotReturnSuccessful204() { using var factory = new AuthServerFactory(); - var admin = await factory.CreateLoginUserAsync(); - var target = await factory.CreateLoginUserAsync(); - await factory.GrantPermissionsAsync(admin.UserKey, [UAuthActions.Credentials.DeleteAdmin]); + var admin = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + await factory.GrantPermissionsAsync(admin.UserKey, [UAuthActions.Credentials.DeleteAdmin], TestContext.Current.CancellationToken); using var client = CreateClient(factory); await AuthenticateAsync(client, admin); using var response = await CsrfTestClient.PostJsonAsync(client, diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/Security/CsrfAttackTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/Security/CsrfAttackTests.cs index 1050c53a..52d4e724 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/Security/CsrfAttackTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/Security/CsrfAttackTests.cs @@ -13,7 +13,7 @@ public sealed class CsrfAttackTests public async Task Login_WithoutCsrfToken_MustNotCreateSession() { await using var factory = new AuthServerFactory(); - var attacker = await factory.CreateLoginUserAsync(); + var attacker = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var victimBrowser = CreateBrowser(factory); @@ -23,7 +23,7 @@ public async Task Login_WithoutCsrfToken_MustNotCreateSession() { identifier = attacker.Identifier, secret = attacker.Secret - }); + }, cancellationToken: TestContext.Current.CancellationToken); await AssertCsrfRejected(response); AssertNoSessionCookie(response); @@ -33,7 +33,7 @@ public async Task Login_WithoutCsrfToken_MustNotCreateSession() public async Task Login_WithForgedCsrfToken_MustNotCreateSession() { await using var factory = new AuthServerFactory(); - var attacker = await factory.CreateLoginUserAsync(); + var attacker = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var victimBrowser = CreateBrowser(factory); @@ -51,7 +51,7 @@ public async Task Login_WithForgedCsrfToken_MustNotCreateSession() "RequestVerificationToken", "attacker-forged-token"); - using var response = await victimBrowser.SendAsync(request); + using var response = await victimBrowser.SendAsync(request, TestContext.Current.CancellationToken); await AssertCsrfRejected(response); AssertNoSessionCookie(response); @@ -61,7 +61,7 @@ public async Task Login_WithForgedCsrfToken_MustNotCreateSession() public async Task Login_WithTokenFromAnotherBrowser_MustBeRejected() { await using var factory = new AuthServerFactory(); - var attacker = await factory.CreateLoginUserAsync(); + var attacker = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var attackerBrowser = CreateBrowser(factory); using var victimBrowser = CreateBrowser(factory); @@ -89,7 +89,7 @@ public async Task Login_WithTokenFromAnotherBrowser_MustBeRejected() "Cookie", victimTokens.Cookie); - using var response = await victimBrowser.SendAsync(request); + using var response = await victimBrowser.SendAsync(request, TestContext.Current.CancellationToken); await AssertCsrfRejected(response); AssertNoSessionCookie(response); @@ -99,7 +99,7 @@ public async Task Login_WithTokenFromAnotherBrowser_MustBeRejected() public async Task Login_WithValidCsrfToken_ShouldSucceed() { await using var factory = new AuthServerFactory(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var browser = CreateBrowser(factory); var tokens = await GetTokens(browser); @@ -122,7 +122,7 @@ public async Task Login_WithValidCsrfToken_ShouldSucceed() "Cookie", tokens.Cookie); - using var response = await browser.SendAsync(request); + using var response = await browser.SendAsync(request, TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.Found); @@ -138,7 +138,7 @@ public async Task Login_WithValidCsrfToken_ShouldSucceed() public async Task RevokeOthers_CsrfAttack_MustNotRevokeSessions() { await using var factory = new AuthServerFactory(); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var currentBrowser = CreateBrowser(factory); using var otherBrowser = CreateBrowser(factory); @@ -159,7 +159,7 @@ public async Task RevokeOthers_CsrfAttack_MustNotRevokeSessions() // ATTACK 1: Authenticated request without CSRF token. using (var missingToken = await currentBrowser.PostAsync( - "/auth/me/sessions/revoke-others", null)) + "/auth/me/sessions/revoke-others", null, TestContext.Current.CancellationToken)) { await AssertCsrfRejected(missingToken); } @@ -177,7 +177,7 @@ public async Task RevokeOthers_CsrfAttack_MustNotRevokeSessions() "forged-token"); using var forged = await currentBrowser.SendAsync( - forgedRequest); + forgedRequest, TestContext.Current.CancellationToken); await AssertCsrfRejected(forged); } @@ -192,7 +192,7 @@ public async Task RevokeOthers_CsrfAttack_MustNotRevokeSessions() { valid.IsSuccessStatusCode.Should().BeTrue( $"valid revoke failed: " + - await valid.Content.ReadAsStringAsync()); + await valid.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)); } // The current session must survive revoke-others. diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/Security/UAuthAntiforgeryEndpointTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/Security/UAuthAntiforgeryEndpointTests.cs index a926f892..c9c4cc07 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/Security/UAuthAntiforgeryEndpointTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/Security/UAuthAntiforgeryEndpointTests.cs @@ -21,7 +21,7 @@ public async Task TokenEndpoint_ReturnsValidTokenAndNoStore() using var client = factory.CreateClient(); using var response = await client.GetAsync( - "/auth/antiforgery/token"); + "/auth/antiforgery/token", TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.OK, response.StatusCode); @@ -30,7 +30,7 @@ public async Task TokenEndpoint_ReturnsValidTokenAndNoStore() response.Headers.CacheControl?.ToString() ?? ""); var token = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); Assert.NotNull(token); Assert.False(string.IsNullOrWhiteSpace(token.RequestToken)); @@ -62,11 +62,11 @@ public async Task Login_WithoutToken_IsRejected() { identifier = "csrf-test-user", secret = "irrelevant" - }); + }, cancellationToken: TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - var body = await response.Content.ReadAsStringAsync(); + var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); Assert.Contains( "invalid_antiforgery_token", @@ -85,7 +85,7 @@ public async Task Login_WithInvalidToken_IsRejected() HandleCookies = true }); - await client.GetAsync("/auth/antiforgery/token"); + await client.GetAsync("/auth/antiforgery/token", TestContext.Current.CancellationToken); using var request = new HttpRequestMessage( HttpMethod.Post, @@ -102,7 +102,7 @@ public async Task Login_WithInvalidToken_IsRejected() "X-UAuth-CSRF", "invalid-token"); - using var response = await client.SendAsync(request); + using var response = await client.SendAsync(request, TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); } @@ -120,7 +120,7 @@ public async Task Login_WithValidToken_PassesCsrfValidation() }); var token = await client.GetFromJsonAsync( - "/auth/antiforgery/token"); + "/auth/antiforgery/token", cancellationToken: TestContext.Current.CancellationToken); Assert.NotNull(token); @@ -139,9 +139,9 @@ public async Task Login_WithValidToken_PassesCsrfValidation() token.HeaderName, token.RequestToken); - using var response = await client.SendAsync(request); + using var response = await client.SendAsync(request, TestContext.Current.CancellationToken); - var body = await response.Content.ReadAsStringAsync(); + var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); Assert.DoesNotContain( "invalid_antiforgery_token", @@ -169,10 +169,10 @@ public async Task TokenFromAnotherClient_IsRejected() var firstToken = await firstClient.GetFromJsonAsync( - "/auth/antiforgery/token"); + "/auth/antiforgery/token", cancellationToken: TestContext.Current.CancellationToken); // Ensure the second client has its own antiforgery cookie. - await secondClient.GetAsync("/auth/antiforgery/token"); + await secondClient.GetAsync("/auth/antiforgery/token", TestContext.Current.CancellationToken); Assert.NotNull(firstToken); @@ -191,11 +191,11 @@ await firstClient.GetFromJsonAsync( firstToken.HeaderName, firstToken.RequestToken); - using var response = await secondClient.SendAsync(request); + using var response = await secondClient.SendAsync(request, TestContext.Current.CancellationToken); Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode); - var body = await response.Content.ReadAsStringAsync(); + var body = await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken); Assert.Contains( "invalid_antiforgery_token", diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/SessionAdminTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/SessionAdminTests.cs index 8784d760..c6452ad3 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/SessionAdminTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/SessionAdminTests.cs @@ -23,15 +23,15 @@ public async Task ListChainsAdmin_ShouldReturnTargetUsersChains() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); - var unrelated = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var unrelated = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Sessions.ListChainsAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"session-admin-list-{Guid.NewGuid():N}"); @@ -76,12 +76,12 @@ await LoginAsync( { PageNumber = 1, PageSize = 50 - }); + }, cancellationToken: TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.OK); var page = await response.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); page.Should().NotBeNull(); @@ -103,14 +103,14 @@ public async Task GetChainAdmin_ShouldReturnTargetUsersOwnedChain() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Sessions.GetChainAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"session-admin-detail-{Guid.NewGuid():N}"); @@ -143,7 +143,7 @@ await LoginAsync( targetChains.StatusCode.Should().Be(HttpStatusCode.OK); var page = await targetChains.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); page.Should().NotBeNull(); @@ -152,12 +152,12 @@ await LoginAsync( var response = await adminClient.PostAsync( $"/auth/admin/users/{target.UserKey.Value}/sessions/chains/{targetChain.ChainId.Value}", - null); + null, TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.OK); var detail = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); detail.Should().NotBeNull(); @@ -176,16 +176,16 @@ public async Task GetChainAdmin_ShouldRejectChainOwnedByDifferentUser() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); - var targetA = await _factory.CreateLoginUserAsync(); - var targetB = await _factory.CreateLoginUserAsync(); + var targetA = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var targetB = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Sessions.GetChainAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"session-admin-cross-user-{Guid.NewGuid():N}"); @@ -233,7 +233,7 @@ await LoginAsync( await GetChainsAsync(targetBClient); var targetBPage = await targetBChains.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); targetBPage.Should().NotBeNull(); @@ -245,7 +245,7 @@ await LoginAsync( // var response = await adminClient.PostAsync( $"/auth/admin/users/{targetA.UserKey.Value}/sessions/chains/{targetBChain.ChainId.Value}", - null); + null, TestContext.Current.CancellationToken); response.StatusCode.Should().BeOneOf( HttpStatusCode.BadRequest, @@ -274,8 +274,8 @@ public async Task SessionAdminQueryEndpoints_ShouldRequireAdminPermission() { _factory.Clock.Reset(); - var actor = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var actor = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var actorClient = CreateClient( $"session-admin-no-permission-{Guid.NewGuid():N}"); @@ -307,7 +307,7 @@ await LoginAsync( await GetChainsAsync(targetClient); var targetPage = await targetChains.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); targetPage.Should().NotBeNull(); @@ -316,7 +316,7 @@ await LoginAsync( var list = await actorClient.PostAsJsonAsync( $"/auth/admin/users/{target.UserKey.Value}/sessions/chains", - new PageRequest()); + new PageRequest(), cancellationToken: TestContext.Current.CancellationToken); list.StatusCode.Should().BeOneOf( HttpStatusCode.Forbidden, @@ -324,7 +324,7 @@ await LoginAsync( var detail = await actorClient.PostAsync( $"/auth/admin/users/{target.UserKey.Value}/sessions/chains/{targetChain.ChainId.Value}", - null); + null, TestContext.Current.CancellationToken); detail.StatusCode.Should().BeOneOf( HttpStatusCode.Forbidden, @@ -343,7 +343,7 @@ await LoginAsync( [Fact] public async Task SessionAdminQueryEndpoints_ShouldRejectUnauthenticatedRequests() { - var target = await _factory.CreateLoginUserAsync(); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var anonymous = CreateClient( $"session-admin-anonymous-{Guid.NewGuid():N}"); @@ -352,14 +352,14 @@ public async Task SessionAdminQueryEndpoints_ShouldRejectUnauthenticatedRequests var list = await anonymous.PostAsJsonAsync( $"/auth/admin/users/{target.UserKey.Value}/sessions/chains", - new PageRequest()); + new PageRequest(), cancellationToken: TestContext.Current.CancellationToken); list.StatusCode.Should() .Be(HttpStatusCode.Unauthorized); var detail = await anonymous.PostAsync( $"/auth/admin/users/{target.UserKey.Value}/sessions/chains/{randomChain.Value}", - null); + null, TestContext.Current.CancellationToken); detail.StatusCode.Should() .Be(HttpStatusCode.Unauthorized); @@ -370,14 +370,14 @@ public async Task RevokeSessionAdmin_ShouldRevokeTargetUsersSession() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Sessions.RevokeSessionAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"session-revoke-admin-{Guid.NewGuid():N}"); @@ -405,7 +405,7 @@ await LoginAsync( chainsResponse.StatusCode.Should().Be(HttpStatusCode.OK); var page = await chainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); page.Should().NotBeNull(); @@ -439,15 +439,15 @@ public async Task RevokeSessionAdmin_ShouldRejectSessionOwnedByDifferentUser() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var targetA = await _factory.CreateLoginUserAsync(); - var targetB = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var targetA = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var targetB = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Sessions.RevokeSessionAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"session-revoke-cross-admin-{Guid.NewGuid():N}"); @@ -488,7 +488,7 @@ await LoginAsync( bChainsResponse.StatusCode.Should().Be(HttpStatusCode.OK); var bPage = await bChainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); bPage.Should().NotBeNull(); @@ -503,7 +503,7 @@ await LoginAsync( // var attack = await adminClient.PostAsync( $"/auth/admin/users/{targetA.UserKey.Value}/sessions/{bSessionId.Value}/revoke", - null); + null, TestContext.Current.CancellationToken); attack.StatusCode.Should().BeOneOf( HttpStatusCode.BadRequest, @@ -527,8 +527,8 @@ public async Task RevokeSessionAdmin_ShouldRequirePermission() { _factory.Clock.Reset(); - var actor = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var actor = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var actorClient = CreateClient( $"session-revoke-no-permission-{Guid.NewGuid():N}"); @@ -554,7 +554,7 @@ await LoginAsync( var chainsResponse = await GetChainsAsync(targetClient); var page = await chainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); page.Should().NotBeNull(); @@ -586,14 +586,14 @@ public async Task RevokeSessionAdmin_ShouldNotAffectOtherSessionOfSameUser() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Sessions.RevokeSessionAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"session-single-admin-{Guid.NewGuid():N}"); @@ -632,7 +632,7 @@ await LoginAsync( var chainsResponse = await GetChainsAsync(targetClient1); var page = await chainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); page.Should().NotBeNull(); @@ -676,14 +676,14 @@ public async Task RevokeChainAdmin_ShouldRevokeTargetChain() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Sessions.RevokeChainAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"revoke-chain-admin-{Guid.NewGuid():N}"); @@ -711,7 +711,7 @@ await LoginAsync( chainsResponse.StatusCode.Should().Be(HttpStatusCode.OK); var page = await chainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); page.Should().NotBeNull(); @@ -748,15 +748,15 @@ public async Task RevokeChainAdmin_ShouldRejectChainOwnedByDifferentUser() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var targetA = await _factory.CreateLoginUserAsync(); - var targetB = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var targetA = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var targetB = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Sessions.RevokeChainAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"revoke-chain-cross-admin-{Guid.NewGuid():N}"); @@ -796,7 +796,7 @@ await LoginAsync( .Be(HttpStatusCode.OK); var bPage = await bChainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); bPage.Should().NotBeNull(); @@ -809,7 +809,7 @@ await LoginAsync( // var attack = await adminClient.PostAsync( $"/auth/admin/users/{targetA.UserKey.Value}/sessions/chains/{bChain.ChainId.Value}/revoke", - null); + null, TestContext.Current.CancellationToken); attack.StatusCode.Should().BeOneOf( HttpStatusCode.BadRequest, @@ -843,14 +843,14 @@ public async Task RevokeChainAdmin_ShouldNotAffectOtherChainOfSameUser() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, [ UAuthActions.Sessions.RevokeChainAdmin - ]); + ], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"revoke-chain-isolation-admin-{Guid.NewGuid():N}"); @@ -890,7 +890,7 @@ await LoginAsync( await GetChainsAsync(targetClient1); var page = await chainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); page.Should().NotBeNull(); @@ -939,8 +939,8 @@ public async Task RevokeChainAdmin_ShouldRequirePermissionWithoutMutation() { _factory.Clock.Reset(); - var actor = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var actor = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var actorClient = CreateClient( $"revoke-chain-denied-actor-{Guid.NewGuid():N}"); @@ -967,7 +967,7 @@ await LoginAsync( await GetChainsAsync(targetClient); var page = await chainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); page.Should().NotBeNull(); @@ -997,12 +997,12 @@ public async Task RevokeAllChainsAdmin_ShouldRevokeAllTargetUsersChains() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, - [UAuthActions.Sessions.RevokeAllChainsAdmin]); + [UAuthActions.Sessions.RevokeAllChainsAdmin], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"revoke-all-admin-{Guid.NewGuid():N}"); @@ -1071,13 +1071,13 @@ public async Task RevokeAllChainsAdmin_ShouldNotAffectDifferentUser() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); - var unrelated = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var unrelated = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, - [UAuthActions.Sessions.RevokeAllChainsAdmin]); + [UAuthActions.Sessions.RevokeAllChainsAdmin], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"revoke-all-isolation-admin-{Guid.NewGuid():N}"); @@ -1140,8 +1140,8 @@ public async Task RevokeAllChainsAdmin_ShouldRequirePermissionWithoutMutation() { _factory.Clock.Reset(); - var actor = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var actor = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var actorClient = CreateClient( $"revoke-all-denied-actor-{Guid.NewGuid():N}"); @@ -1197,7 +1197,7 @@ public async Task RevokeAllChainsAdmin_ShouldRejectUnauthenticatedRequestWithout { _factory.Clock.Reset(); - var target = await _factory.CreateLoginUserAsync(); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var anonymousClient = CreateClient( $"revoke-all-anonymous-{Guid.NewGuid():N}"); @@ -1235,12 +1235,12 @@ public async Task RevokeRootAdmin_ShouldInvalidateAllExistingTargetSessions() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, - [UAuthActions.Sessions.RevokeRootAdmin]); + [UAuthActions.Sessions.RevokeRootAdmin], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"root-admin-{Guid.NewGuid():N}"); @@ -1302,13 +1302,13 @@ public async Task RevokeRootAdmin_ShouldNotAffectDifferentUser() { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); - var unrelated = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var unrelated = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, - [UAuthActions.Sessions.RevokeRootAdmin]); + [UAuthActions.Sessions.RevokeRootAdmin], TestContext.Current.CancellationToken); using var adminClient = CreateClient( $"root-isolation-admin-{Guid.NewGuid():N}"); @@ -1362,8 +1362,8 @@ public async Task RevokeRootAdmin_ShouldRequirePermissionWithoutMutation() { _factory.Clock.Reset(); - var actor = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var actor = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var actorClient = CreateClient( $"root-denied-actor-{Guid.NewGuid():N}"); @@ -1394,7 +1394,7 @@ public async Task RevokeRootAdmin_ShouldRejectUnauthenticatedRequestWithoutMutat { _factory.Clock.Reset(); - var target = await _factory.CreateLoginUserAsync(); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var anonymousClient = CreateClient($"root-anonymous-{Guid.NewGuid():N}"); using var targetClient = CreateClient($"root-anonymous-target-{Guid.NewGuid():N}"); @@ -1413,10 +1413,10 @@ public async Task RevokeRootAdmin_ShouldAllowSubsequentLoginWithNewAuthenticatio { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); - await _factory.GrantPermissionsAsync(admin.UserKey, [UAuthActions.Sessions.RevokeRootAdmin]); + await _factory.GrantPermissionsAsync(admin.UserKey, [UAuthActions.Sessions.RevokeRootAdmin], TestContext.Current.CancellationToken); var adminDevice = $"root-recreate-admin-{Guid.NewGuid():N}"; @@ -1479,12 +1479,12 @@ public async Task RevokeRootAdmin_ConcurrentSubsequentLogins_ShouldCreateSingleA { _factory.Clock.Reset(); - var admin = await _factory.CreateLoginUserAsync(); - var target = await _factory.CreateLoginUserAsync(); + var admin = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await _factory.GrantPermissionsAsync( admin.UserKey, - [UAuthActions.Sessions.RevokeRootAdmin]); + [UAuthActions.Sessions.RevokeRootAdmin], TestContext.Current.CancellationToken); using var adminClient = CreateClient($"root-concurrent-admin-{Guid.NewGuid():N}"); @@ -1579,7 +1579,7 @@ await _factory.GrantPermissionsAsync( verificationB.StatusCode.Should().Be(HttpStatusCode.OK); var chainsA = await verificationA.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); chainsA.Should().NotBeNull(); @@ -1592,7 +1592,7 @@ await _factory.GrantPermissionsAsync( .Should().Be(1); var chainsB = await verificationB.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); chainsB.Should().NotBeNull(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/SessionSlidingExpirationTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/SessionSlidingExpirationTests.cs index 3a914d4f..5f84aba7 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/SessionSlidingExpirationTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/SessionSlidingExpirationTests.cs @@ -132,7 +132,7 @@ public async Task IdentifierGet_ExpiredIdleSession_ShouldReturnUnauthorized(stri o.Session.TouchInterval = TimeSpan.FromSeconds(5); }); - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = factory.CreateClient( new WebApplicationFactoryClientOptions @@ -163,7 +163,7 @@ public async Task IdentifierGet_ExpiredIdleSession_ShouldReturnUnauthorized(stri // Same session must be accepted before IdleTimeout. using var before = await client.PostAsJsonAsync( - "/auth/me/identifiers/get", new { }); + "/auth/me/identifiers/get", new { }, cancellationToken: TestContext.Current.CancellationToken); before.StatusCode.Should().Be(HttpStatusCode.OK); @@ -172,7 +172,7 @@ public async Task IdentifierGet_ExpiredIdleSession_ShouldReturnUnauthorized(stri // Keep the original cookie deliberately. using var after = await client.PostAsJsonAsync( - "/auth/me/identifiers/get", new { }); + "/auth/me/identifiers/get", new { }, cancellationToken: TestContext.Current.CancellationToken); after.StatusCode.Should().Be(HttpStatusCode.Unauthorized, "an expired session must not remain authenticated " + diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/SessionTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/SessionTests.cs index 9b5efad1..c029a1a3 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/SessionTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/SessionTests.cs @@ -23,8 +23,8 @@ public async Task ListChainsSelf_ShouldReturnOnlyAuthenticatedUsersChains() { _factory.Clock.Reset(); - var user1 = await _factory.CreateLoginUserAsync(); - var user2 = await _factory.CreateLoginUserAsync(); + var user1 = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var user2 = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var user1Device1 = CreateClient( $"session-user1-device1-{Guid.NewGuid():N}"); @@ -57,7 +57,7 @@ await LoginAsync( response.StatusCode.Should().Be(HttpStatusCode.OK); var page = await response.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); page.Should().NotBeNull(); @@ -70,7 +70,7 @@ public async Task GetChainSelf_ShouldReturnRequestedOwnedChain() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client = CreateClient( $"session-detail-{Guid.NewGuid():N}"); @@ -90,7 +90,7 @@ await LoginAsync( chainsResponse.StatusCode.Should().Be(HttpStatusCode.OK); var page = await chainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); page.Should().NotBeNull(); @@ -98,12 +98,12 @@ await LoginAsync( var detailResponse = await client.PostAsync( $"/auth/me/sessions/chains/{chain.ChainId.Value}", - null); + null, TestContext.Current.CancellationToken); detailResponse.StatusCode.Should().Be(HttpStatusCode.OK); var detail = await detailResponse.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); detail.Should().NotBeNull(); detail!.ChainId.Should().Be(chain.ChainId); @@ -116,8 +116,8 @@ public async Task GetChainSelf_ShouldNotAllowReadingAnotherUsersChain() { _factory.Clock.Reset(); - var attacker = await _factory.CreateLoginUserAsync(); - var victim = await _factory.CreateLoginUserAsync(); + var attacker = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var victim = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var attackerClient = CreateClient( $"session-attacker-{Guid.NewGuid():N}"); @@ -149,7 +149,7 @@ await LoginAsync( await GetChainsAsync(victimClient); var victimChains = await victimChainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); victimChains.Should().NotBeNull(); @@ -158,7 +158,7 @@ await LoginAsync( var attack = await attackerClient.PostAsync( $"/auth/me/sessions/chains/{victimChain.ChainId.Value}", - null); + null, TestContext.Current.CancellationToken); attack.StatusCode.Should().BeOneOf( HttpStatusCode.BadRequest, @@ -179,7 +179,7 @@ public async Task RevokeChainSelf_ShouldRevokeOwnedTargetChain() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var currentDevice = $"session-current-{Guid.NewGuid():N}"; @@ -214,7 +214,7 @@ await LoginAsync( await GetChainsAsync(targetClient); var targetChains = await targetChainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); targetChains.Should().NotBeNull(); @@ -247,8 +247,8 @@ public async Task RevokeChainSelf_ShouldNotAllowRevokingAnotherUsersChain() { _factory.Clock.Reset(); - var attacker = await _factory.CreateLoginUserAsync(); - var victim = await _factory.CreateLoginUserAsync(); + var attacker = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var victim = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var attackerClient = CreateClient( $"session-revoke-attacker-{Guid.NewGuid():N}"); @@ -280,7 +280,7 @@ await LoginAsync( await GetChainsAsync(victimClient); var victimChains = await victimChainsResponse.Content - .ReadFromJsonAsync>(); + .ReadFromJsonAsync>(cancellationToken: TestContext.Current.CancellationToken); victimChains.Should().NotBeNull(); @@ -289,7 +289,7 @@ await LoginAsync( var attack = await attackerClient.PostAsync( $"/auth/me/sessions/chains/{victimChain.ChainId.Value}/revoke", - null); + null, TestContext.Current.CancellationToken); attack.StatusCode.Should().BeOneOf( HttpStatusCode.BadRequest, @@ -334,7 +334,7 @@ public async Task SessionSelfEndpoints_ShouldRejectUnauthenticatedRequests() var detail = await client.PostAsync( $"/auth/me/sessions/chains/{randomChain.Value}", - null); + null, TestContext.Current.CancellationToken); detail.StatusCode.Should() .Be(HttpStatusCode.Unauthorized); @@ -366,7 +366,7 @@ public async Task RevokeOthersSelf_ShouldRevokeAllOtherChainsButKeepCurrentChain { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var currentClient = CreateClient( $"revoke-others-current-{Guid.NewGuid():N}"); @@ -443,8 +443,8 @@ public async Task RevokeOthersSelf_ShouldNotAffectAnotherUsersChains() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); - var unrelatedUser = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var unrelatedUser = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var currentClient = CreateClient( $"revoke-others-owner-current-{Guid.NewGuid():N}"); @@ -524,7 +524,7 @@ public async Task RevokeAllSelf_ShouldRevokeCurrentAndAllOtherChains() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var client1 = CreateClient( $"revoke-all-1-{Guid.NewGuid():N}"); @@ -583,8 +583,8 @@ public async Task RevokeAllSelf_ShouldNotAffectAnotherUser() { _factory.Clock.Reset(); - var user = await _factory.CreateLoginUserAsync(); - var unrelatedUser = await _factory.CreateLoginUserAsync(); + var user = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var unrelatedUser = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var userClient = CreateClient( $"revoke-all-owner-{Guid.NewGuid():N}"); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/UAuthHubSampleSmokeTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/UAuthHubSampleSmokeTests.cs index f657bc1d..8ee068c6 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/UAuthHubSampleSmokeTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/UAuthHubSampleSmokeTests.cs @@ -20,7 +20,7 @@ public UAuthHubSampleSmokeTests(AuthServerFactory factory) [InlineData("/login")] public async Task CriticalPages_ShouldRenderWithoutServerError(string path) { - var response = await _client.GetAsync(path); + var response = await _client.GetAsync(path, TestContext.Current.CancellationToken); ((int)response.StatusCode) .Should() diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/UserCreationAtomicityTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/UserCreationAtomicityTests.cs index 071e052c..10e528a9 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/UserCreationAtomicityTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/UserCreationAtomicityTests.cs @@ -72,7 +72,7 @@ public async Task CreateUser_WhenPersistenceFails_ShouldRollbackAllUserState() var exception = await Assert.ThrowsAsync( () => service.CreateUserAsync( context, - request)); + request, TestContext.Current.CancellationToken)); exception.Message.Should() .Be("simulated_identifier_store_failure"); @@ -103,11 +103,11 @@ public async Task CreateUser_WhenPersistenceFails_ShouldRollbackAllUserState() var identifierStore = identifierFactory.Create(tenant); - var lifecycle = await lifecycleStore.GetAsync(new UserLifecycleKey(tenant, userKey)); + var lifecycle = await lifecycleStore.GetAsync(new UserLifecycleKey(tenant, userKey), TestContext.Current.CancellationToken); - var profiles = await profileStore.GetAllProfilesByUserAsync(userKey); + var profiles = await profileStore.GetAllProfilesByUserAsync(userKey, TestContext.Current.CancellationToken); - var identifiers = await identifierStore.GetByUserAsync(userKey); + var identifiers = await identifierStore.GetByUserAsync(userKey, TestContext.Current.CancellationToken); lifecycle.Should().BeNull("failed user creation must not leave a lifecycle aggregate"); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/UserIdentifierTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/UserIdentifierTests.cs index 7bd0af39..ab0d052a 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/UserIdentifierTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/UserIdentifierTests.cs @@ -35,11 +35,11 @@ public async Task Availability_WhenUsernameDoesNotExist_ShouldReturnAvailable() { Type = UserIdentifierType.Username, Value = username - }); + }, cancellationToken: TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.OK); - var result = await response.Content.ReadFromJsonAsync(); + var result = await response.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -64,7 +64,7 @@ public async Task Availability_WhenUsernameAlreadyExists_ShouldReturnUnavailable UserName = username, Password = $"Test-{Guid.NewGuid():N}!", DisplayName = username - }); + }, cancellationToken: TestContext.Current.CancellationToken); create.StatusCode.Should().Be(HttpStatusCode.OK); @@ -74,11 +74,11 @@ public async Task Availability_WhenUsernameAlreadyExists_ShouldReturnUnavailable { Type = UserIdentifierType.Username, Value = username - }); + }, cancellationToken: TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.OK); - var result = await response.Content.ReadFromJsonAsync(); + var result = await response.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -105,7 +105,7 @@ public async Task Availability_ShouldUseNormalizedIdentifier() Email = email, Password = $"Test-{Guid.NewGuid():N}!", DisplayName = "Availability Test" - }); + }, cancellationToken: TestContext.Current.CancellationToken); create.StatusCode.Should().Be(HttpStatusCode.OK); @@ -116,11 +116,11 @@ public async Task Availability_ShouldUseNormalizedIdentifier() { Type = UserIdentifierType.Email, Value = $" {email.ToUpperInvariant()} " - }); + }, cancellationToken: TestContext.Current.CancellationToken); response.StatusCode.Should().Be(HttpStatusCode.OK); - var result = await response.Content.ReadFromJsonAsync(); + var result = await response.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -144,14 +144,14 @@ public async Task Availability_WhenIdentifierIsInvalid_ShouldReturnValidationRes { Type = UserIdentifierType.Email, Value = "not-an-email" - }); + }, cancellationToken: TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.OK); var result = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -173,14 +173,14 @@ public async Task Availability_WhenIdentifierIsEmpty_ShouldReturnValidationResul { Type = UserIdentifierType.Username, Value = " " - }); + }, cancellationToken: TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.OK); var result = await response.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -202,10 +202,10 @@ await response.Content [InlineData("delete", true)] public async Task IdentifierMutation_WhenIdBelongsToAnotherUser_ShouldReturnNotFoundAndPreserveRecords(string operation, bool admin) { - var actor = await _factory.CreateLoginUserAsync(); - var victim = await _factory.CreateLoginUserAsync(); + var actor = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); + var victim = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); if (admin) - await _factory.GrantPermissionsAsync(actor.UserKey, new[] { "*" }); + await _factory.GrantPermissionsAsync(actor.UserKey, new[] { "*" }, TestContext.Current.CancellationToken); using var scope = _factory.Services.CreateScope(); var services = scope.ServiceProvider; @@ -220,16 +220,16 @@ public async Task IdentifierMutation_WhenIdBelongsToAnotherUser_ShouldReturnNotF _factory.Clock.UtcNow, isPrimary: operation == "unset-primary", verifiedAt: operation == "verify" ? null : _factory.Clock.UtcNow); - await store.AddAsync(identifier); + await store.AddAsync(identifier, TestContext.Current.CancellationToken); - var before = (await store.GetByUserAsync(victim.UserKey)) + var before = (await store.GetByUserAsync(victim.UserKey, TestContext.Current.CancellationToken)) .Select(x => x.Snapshot()).ToArray(); using var client = CreateClient(); await AuthenticateIdentifierClientAsync(client, actor.Identifier, actor.Secret); // Prove authentication works before exercising the ownership boundary. using var authenticated = await client.PostAsJsonAsync( - "/auth/me/identifiers/get", new { }); + "/auth/me/identifiers/get", new { }, cancellationToken: TestContext.Current.CancellationToken); authenticated.StatusCode.Should().Be(HttpStatusCode.OK); var path = admin @@ -240,7 +240,7 @@ public async Task IdentifierMutation_WhenIdBelongsToAnotherUser_ShouldReturnNotF // Use a separate route target whose identifiers are NOT the victim's. if (admin) { - var target = await _factory.CreateLoginUserAsync(); + var target = await _factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); path = $"/auth/admin/users/{target.UserKey.Value}/identifiers/{operation}"; } @@ -252,7 +252,7 @@ public async Task IdentifierMutation_WhenIdBelongsToAnotherUser_ShouldReturnNotF mode = 0 }); - var after = await store.GetByUserAsync(victim.UserKey); + var after = await store.GetByUserAsync(victim.UserKey, TestContext.Current.CancellationToken); using var assertions = new AwesomeAssertions.Execution.AssertionScope(); response.StatusCode.Should().Be(HttpStatusCode.NotFound); after.Should().BeEquivalentTo(before, @@ -274,7 +274,7 @@ public async Task IdentifierMutation_WhenIdBelongsToSelf_ShouldSucceed(string op var factory = configuredFactory ?? _factory; - var owner = await factory.CreateLoginUserAsync(); + var owner = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var scope = factory.Services.CreateScope(); var services = scope.ServiceProvider; var store = services.GetRequiredService().Create(TenantKeys.Single); @@ -287,7 +287,7 @@ public async Task IdentifierMutation_WhenIdBelongsToSelf_ShouldSucceed(string op factory.Clock.UtcNow, isPrimary: operation == "unset-primary", verifiedAt: operation == "verify" ? null : factory.Clock.UtcNow); - await store.AddAsync(identifier); + await store.AddAsync(identifier, TestContext.Current.CancellationToken); using var client = factory.CreateClient( new WebApplicationFactoryClientOptions { @@ -315,8 +315,8 @@ public async Task IdentifierMutation_WhenIdBelongsToSelf_ShouldSucceed(string op : null }); response.IsSuccessStatusCode.Should().BeTrue( - $"own identifier operation should succeed: {await response.Content.ReadAsStringAsync()}"); - var saved = await store.GetByIdAsync(identifier.Id); + $"own identifier operation should succeed: {await response.Content.ReadAsStringAsync(TestContext.Current.CancellationToken)}"); + var saved = await store.GetByIdAsync(identifier.Id, TestContext.Current.CancellationToken); saved.Should().NotBeNull(); switch (operation) { diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/UserLifecycleTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/UserLifecycleTests.cs index 2723e5f7..f659f431 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/UserLifecycleTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/UserLifecycleTests.cs @@ -42,13 +42,13 @@ public async Task CreateUser_ShouldCreateUsableUser() UserName = username, Password = secret, DisplayName = "Integration User" - }); + }, cancellationToken: TestContext.Current.CancellationToken); response.StatusCode.Should() .Be(HttpStatusCode.OK); var result = - await response.Content.ReadFromJsonAsync(); + await response.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.IsSuccess.Should().BeTrue(); @@ -100,7 +100,7 @@ public async Task CreateUser_ShouldCreateLifecycle() var lifecycle = await store.GetAsync( new UserLifecycleKey( TenantKeys.Single, - userKey)); + userKey), TestContext.Current.CancellationToken); lifecycle.Should().NotBeNull(); @@ -151,7 +151,7 @@ public async Task CreateUser_ShouldCreateDefaultProfile() new UserProfileKey( TenantKeys.Single, userKey, - ProfileKey.Default)); + ProfileKey.Default), TestContext.Current.CancellationToken); profile.Should().NotBeNull(); @@ -196,7 +196,7 @@ public async Task CreateUser_WithUsername_ShouldCreatePrimaryUsername() var identifiers = await store.GetByUserAsync( - userKey); + userKey, TestContext.Current.CancellationToken); var identifier = identifiers .Single(x => @@ -233,7 +233,7 @@ public async Task CreateUser_WithVerifiedEmail_ShouldNotPersistEmailAsVerified() using var scope = _factory.Services.CreateScope(); var factory = scope.ServiceProvider.GetRequiredService(); var store = factory.Create(TenantKeys.Single); - var identifiers = await store.GetByUserAsync(userKey); + var identifiers = await store.GetByUserAsync(userKey, TestContext.Current.CancellationToken); var identifier = identifiers.Single(x => x.Type == UserIdentifierType.Email); identifier.Value.Should().Be(email); @@ -275,7 +275,7 @@ public async Task CreateUser_WithUnverifiedEmail_ShouldPersistEmailAsUnverified( var identifiers = await store.GetByUserAsync( - userKey); + userKey, TestContext.Current.CancellationToken); var identifier = identifiers .Single(x => @@ -320,7 +320,7 @@ public async Task CreateUser_WithUsernameAndEmail_ShouldCreateBothIdentifiers() var identifiers = await store.GetByUserAsync( - userKey); + userKey, TestContext.Current.CancellationToken); identifiers.Should() .ContainSingle(x => @@ -394,7 +394,7 @@ public async Task CreateUser_WithExistingUsername_ShouldNotCreateSecondUser() var firstResult = await firstResponse.Content - .ReadFromJsonAsync(); + .ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); firstResult.Should().NotBeNull(); firstResult!.IsSuccess.Should().BeTrue(); @@ -415,7 +415,7 @@ await CreateUserResponseAsync( ((int)secondResponse.StatusCode).Should().BeInRange(400, 499); - var problem = await secondResponse.Content.ReadFromJsonAsync(); + var problem = await secondResponse.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); problem.Should().NotBeNull(); @@ -446,7 +446,7 @@ await CreateUserResponseAsync( var identifier = await store.GetAsync( UserIdentifierType.Username, - normalized.Normalized); + normalized.Normalized, TestContext.Current.CancellationToken); identifier.Should().NotBeNull(); @@ -495,7 +495,7 @@ await lifecycleFactory .GetAsync( new UserLifecycleKey( TenantKeys.Single, - userKey)); + userKey), TestContext.Current.CancellationToken); var profile = await profileFactory @@ -504,13 +504,13 @@ await profileFactory new UserProfileKey( TenantKeys.Single, userKey, - ProfileKey.Default)); + ProfileKey.Default), TestContext.Current.CancellationToken); var identifiers = await identifierFactory .Create(TenantKeys.Single) .GetByUserAsync( - userKey); + userKey, TestContext.Current.CancellationToken); lifecycle.Should().NotBeNull(); profile.Should().NotBeNull(); @@ -568,7 +568,7 @@ public async Task QueryUsersAsync_AppliesConfiguredPaginationAndSorting() foreach (var name in names) { - var user = await factory.CreateLoginUserAsync(); + var user = await factory.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); using var seedScope = factory.Services.CreateScope(); @@ -583,7 +583,7 @@ await profileFactory user.UserKey, ProfileKey.Default, factory.Clock.UtcNow, - displayName: $"{prefix}-{name}")); + displayName: $"{prefix}-{name}"), TestContext.Current.CancellationToken); } using var scope = factory.Services.CreateScope(); @@ -616,7 +616,7 @@ await profileFactory PageSize = 0, Search = prefix, SortBy = nameof(UserSummary.DisplayName) - }); + }, TestContext.Current.CancellationToken); firstPage.TotalCount.Should().Be(5); firstPage.PageNumber.Should().Be(1); @@ -637,7 +637,7 @@ await profileFactory PageSize = 100, Search = prefix, SortBy = nameof(UserSummary.DisplayName) - }); + }, TestContext.Current.CancellationToken); secondPage.TotalCount.Should().Be(5); secondPage.PageNumber.Should().Be(2); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Integration/UserProfileTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Integration/UserProfileTests.cs index 791c76f3..5fa6cb94 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Integration/UserProfileTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Integration/UserProfileTests.cs @@ -7,13 +7,15 @@ namespace CodeBeam.UltimateAuth.Tests.Integration; -public class UserProfileTests : IClassFixture +public class UserProfileTests : IDisposable { + // Profiles created by one test must not exist in another test's host. + private readonly AuthServerFactory _factory = new(); private readonly HttpClient _client; - public UserProfileTests(AuthServerFactory factory) + public UserProfileTests() { - _client = factory.CreateClient(new WebApplicationFactoryClientOptions + _client = _factory.CreateClient(new WebApplicationFactoryClientOptions { AllowAutoRedirect = false, HandleCookies = false @@ -23,6 +25,12 @@ public UserProfileTests(AuthServerFactory factory) _client.DefaultRequestHeaders.Add("X-UDID", $"profile-test-{Guid.NewGuid():N}"); } + public void Dispose() + { + _client.Dispose(); + _factory.Dispose(); + } + [Fact] public async Task Profile_Switch_Should_Return_Correct_Profile_Data() { @@ -31,7 +39,7 @@ public async Task Profile_Switch_Should_Return_Correct_Profile_Data() var defaultResponse = await PostProfileAsync("/auth/me/profile/get", new { }); defaultResponse.StatusCode.Should().Be(HttpStatusCode.OK); - var defaultProfile = await defaultResponse.Content.ReadFromJsonAsync(); + var defaultProfile = await defaultResponse.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); defaultProfile.Should().NotBeNull(); var createResponse = await PostProfileAsync("/auth/me/profile/create", new CreateProfileRequest @@ -56,14 +64,14 @@ public async Task Profile_Switch_Should_Return_Correct_Profile_Data() businessResponse.StatusCode.Should().Be(HttpStatusCode.OK); - var businessProfile = await businessResponse.Content.ReadFromJsonAsync(); + var businessProfile = await businessResponse.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); businessProfile.Should().NotBeNull(); businessProfile!.DisplayName.Should().Be("Updated Business Name"); var defaultAgainResponse = await PostProfileAsync("/auth/me/profile/get", new { }); - var defaultAgain = await defaultAgainResponse.Content.ReadFromJsonAsync(); + var defaultAgain = await defaultAgainResponse.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); defaultAgain!.DisplayName.Should().Be(defaultProfile!.DisplayName); } @@ -73,9 +81,9 @@ public async Task GetMe_Without_ProfileKey_Should_Return_Default_Profile() { await Login(); - var response = await _client.PostAsJsonAsync("/auth/me/profile/get", new { }); + var response = await _client.PostAsJsonAsync("/auth/me/profile/get", new { }, cancellationToken: TestContext.Current.CancellationToken); - var profile = await response.Content.ReadFromJsonAsync(); + var profile = await response.Content.ReadFromJsonAsync(cancellationToken: TestContext.Current.CancellationToken); profile.Should().NotBeNull(); profile!.ProfileKey.Value.Should().Be("default"); diff --git a/tests/CodeBeam.UltimateAuth.Tests.RateLimiting.Compatibility/CodeBeam.UltimateAuth.Tests.RateLimiting.Compatibility.csproj b/tests/CodeBeam.UltimateAuth.Tests.RateLimiting.Compatibility/CodeBeam.UltimateAuth.Tests.RateLimiting.Compatibility.csproj index 7fe0d607..104a4c4e 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.RateLimiting.Compatibility/CodeBeam.UltimateAuth.Tests.RateLimiting.Compatibility.csproj +++ b/tests/CodeBeam.UltimateAuth.Tests.RateLimiting.Compatibility/CodeBeam.UltimateAuth.Tests.RateLimiting.Compatibility.csproj @@ -1,17 +1,10 @@ - net8.0;net9.0;net10.0 false - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/AssemblyBehavior.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/AssemblyBehavior.cs index c7fc3b1f..0378b5a6 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/AssemblyBehavior.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/AssemblyBehavior.cs @@ -1 +1,3 @@ -[assembly: CollectionBehavior(DisableTestParallelization = true)] +using Xunit.v3; + +[assembly: Parallelization(Mode = Xunit.Sdk.ParallelMode.None)] diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Authentication/AuthenticationSecurityStateStoreContractTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Authentication/AuthenticationSecurityStateStoreContractTests.cs index b3523c18..25117b45 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Authentication/AuthenticationSecurityStateStoreContractTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Authentication/AuthenticationSecurityStateStoreContractTests.cs @@ -31,12 +31,12 @@ public async Task AddAsync_WhenValid_PersistsState() var state = AuthenticationSecurityState.CreateAccount(Tenant, user); - await store.AddAsync(state); + await store.AddAsync(state, ct: TestContext.Current.CancellationToken); var result = await store.GetAsync( user, AuthenticationSecurityScope.Account, - null); + null, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.Id.Should().Be(state.Id); @@ -64,18 +64,18 @@ public async Task AddAsync_SameUserWithAccountAndFactorStates_IsAllowed() user, CredentialType.Password); - await store.AddAsync(account); - await store.AddAsync(factor); + await store.AddAsync(account, ct: TestContext.Current.CancellationToken); + await store.AddAsync(factor, ct: TestContext.Current.CancellationToken); var accountResult = await store.GetAsync( user, AuthenticationSecurityScope.Account, - null); + null, ct: TestContext.Current.CancellationToken); var factorResult = await store.GetAsync( user, AuthenticationSecurityScope.Factor, - CredentialType.Password); + CredentialType.Password, ct: TestContext.Current.CancellationToken); accountResult.Should().NotBeNull(); factorResult.Should().NotBeNull(); @@ -100,7 +100,7 @@ public async Task AddAsync_WhenSameLogicalStateAlreadyExists_ThrowsConflict() Tenant, user); - await store.AddAsync(first); + await store.AddAsync(first, ct: TestContext.Current.CancellationToken); var act = () => store.AddAsync(second); @@ -137,7 +137,7 @@ public async Task GetAsync_WhenStateDoesNotExist_ReturnsNull() var result = await store.GetAsync( UserKey.New(), AuthenticationSecurityScope.Account, - null); + null, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -158,18 +158,18 @@ public async Task AddAsync_SameUserWithDifferentScope_IsAllowed() var second = AuthenticationSecurityState.CreateFactor(Tenant, user, CredentialType.Password); - await store.AddAsync(first); - await store.AddAsync(second); + await store.AddAsync(first, ct: TestContext.Current.CancellationToken); + await store.AddAsync(second, ct: TestContext.Current.CancellationToken); var firstResult = await store.GetAsync( user, first.Scope, - first.CredentialType); + first.CredentialType, ct: TestContext.Current.CancellationToken); var secondResult = await store.GetAsync( user, second.Scope, - second.CredentialType); + second.CredentialType, ct: TestContext.Current.CancellationToken); firstResult.Should().NotBeNull(); secondResult.Should().NotBeNull(); @@ -190,18 +190,18 @@ public async Task AddAsync_SameUserAndScopeWithDifferentCredentialType_IsAllowed var second = AuthenticationSecurityState.CreateFactor(Tenant, user, CredentialType.Passkey); - await store.AddAsync(first); - await store.AddAsync(second); + await store.AddAsync(first, ct: TestContext.Current.CancellationToken); + await store.AddAsync(second, ct: TestContext.Current.CancellationToken); var password = await store.GetAsync( user, AuthenticationSecurityScope.Factor, - CredentialType.Password); + CredentialType.Password, ct: TestContext.Current.CancellationToken); var passkey = await store.GetAsync( user, AuthenticationSecurityScope.Factor, - CredentialType.Passkey); + CredentialType.Passkey, ct: TestContext.Current.CancellationToken); password.Should().NotBeNull(); passkey.Should().NotBeNull(); @@ -226,7 +226,7 @@ public async Task UpdateAsync_WhenExpectedVersionMatches_PersistsChanges() Tenant, user); - await store.AddAsync(original); + await store.AddAsync(original, ct: TestContext.Current.CancellationToken); var updated = original.RegisterFailure( Now, @@ -236,12 +236,12 @@ public async Task UpdateAsync_WhenExpectedVersionMatches_PersistsChanges() await store.UpdateAsync( updated, - expectedVersion: original.SecurityVersion); + expectedVersion: original.SecurityVersion, ct: TestContext.Current.CancellationToken); var result = await store.GetAsync( user, AuthenticationSecurityScope.Account, - null); + null, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -262,7 +262,7 @@ public async Task UpdateAsync_WhenExpectedVersionIsStale_ThrowsConflict() Tenant, UserKey.New()); - await store.AddAsync(original); + await store.AddAsync(original, ct: TestContext.Current.CancellationToken); var updated = original.RegisterFailure( Now, @@ -291,7 +291,7 @@ public async Task UpdateAsync_WhenResetBegins_PersistsResetState() user, CredentialType.Password); - await store.AddAsync(original); + await store.AddAsync(original, ct: TestContext.Current.CancellationToken); var updated = original.BeginReset( "hashed-reset-token", @@ -300,12 +300,12 @@ public async Task UpdateAsync_WhenResetBegins_PersistsResetState() await store.UpdateAsync( updated, - original.SecurityVersion); + original.SecurityVersion, ct: TestContext.Current.CancellationToken); var result = await store.GetAsync( user, AuthenticationSecurityScope.Factor, - CredentialType.Password); + CredentialType.Password, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -351,17 +351,17 @@ public async Task DeleteAsync_WhenStateExists_RemovesState() var state = AuthenticationSecurityState.CreateAccount(Tenant, user); - await store.AddAsync(state); + await store.AddAsync(state, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( user, state.Scope, - state.CredentialType); + state.CredentialType, ct: TestContext.Current.CancellationToken); var result = await store.GetAsync( user, state.Scope, - state.CredentialType); + state.CredentialType, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -383,23 +383,23 @@ public async Task DeleteAsync_AccountState_DoesNotDeleteFactorState() user, CredentialType.Password); - await store.AddAsync(account); - await store.AddAsync(factor); + await store.AddAsync(account, ct: TestContext.Current.CancellationToken); + await store.AddAsync(factor, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( user, AuthenticationSecurityScope.Account, - null); + null, ct: TestContext.Current.CancellationToken); var accountResult = await store.GetAsync( user, AuthenticationSecurityScope.Account, - null); + null, ct: TestContext.Current.CancellationToken); var factorResult = await store.GetAsync( user, AuthenticationSecurityScope.Factor, - CredentialType.Password); + CredentialType.Password, ct: TestContext.Current.CancellationToken); accountResult.Should().BeNull(); factorResult.Should().NotBeNull(); @@ -430,23 +430,23 @@ public async Task DeleteAsync_RemovesOnlyExactLogicalState() var password = AuthenticationSecurityState.CreateFactor(Tenant, user, CredentialType.Password); var passkey = AuthenticationSecurityState.CreateFactor(Tenant, user, CredentialType.Passkey); - await store.AddAsync(password); - await store.AddAsync(passkey); + await store.AddAsync(password, ct: TestContext.Current.CancellationToken); + await store.AddAsync(passkey, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( user, AuthenticationSecurityScope.Factor, - CredentialType.Password); + CredentialType.Password, ct: TestContext.Current.CancellationToken); var passwordResult = await store.GetAsync( user, AuthenticationSecurityScope.Factor, - CredentialType.Password); + CredentialType.Password, ct: TestContext.Current.CancellationToken); var passkeyResult = await store.GetAsync( user, AuthenticationSecurityScope.Factor, - CredentialType.Passkey); + CredentialType.Passkey, ct: TestContext.Current.CancellationToken); passwordResult.Should().BeNull(); passkeyResult.Should().NotBeNull(); @@ -473,17 +473,17 @@ public async Task GetAsync_IsTenantIsolated() tenantA, user); - await storeA.AddAsync(state); + await storeA.AddAsync(state, ct: TestContext.Current.CancellationToken); var fromA = await storeA.GetAsync( user, AuthenticationSecurityScope.Account, - null); + null, ct: TestContext.Current.CancellationToken); var fromB = await storeB.GetAsync( user, AuthenticationSecurityScope.Account, - null); + null, ct: TestContext.Current.CancellationToken); fromA.Should().NotBeNull(); fromA!.Tenant.Should().Be(tenantA); @@ -512,23 +512,23 @@ public async Task DeleteAsync_IsTenantIsolated() tenantB, user); - await storeA.AddAsync(stateA); - await storeB.AddAsync(stateB); + await storeA.AddAsync(stateA, ct: TestContext.Current.CancellationToken); + await storeB.AddAsync(stateB, ct: TestContext.Current.CancellationToken); await storeA.DeleteAsync( user, AuthenticationSecurityScope.Account, - null); + null, ct: TestContext.Current.CancellationToken); var fromA = await storeA.GetAsync( user, AuthenticationSecurityScope.Account, - null); + null, ct: TestContext.Current.CancellationToken); var fromB = await storeB.GetAsync( user, AuthenticationSecurityScope.Account, - null); + null, ct: TestContext.Current.CancellationToken); fromA.Should().BeNull(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/AuthorizationServiceTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/AuthorizationServiceTests.cs index 727c3797..4caa7741 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/AuthorizationServiceTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/AuthorizationServiceTests.cs @@ -25,7 +25,7 @@ public async Task AuthorizeAsync_WhenAccessOrchestratorAllows_ReturnsAllowed() .Returns, CancellationToken>( async (_, command, ct) => await command.ExecuteAsync(ct)); - var result = await f.Sut.AuthorizeAsync(context); + var result = await f.Sut.AuthorizeAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeTrue(); result.RequiresReauthentication.Should().BeFalse(); @@ -54,7 +54,7 @@ public async Task AuthorizeAsync_WhenAccessOrchestratorDenies_ReturnsDenied() It.IsAny())) .ThrowsAsync(exception); - var result = await f.Sut.AuthorizeAsync(context); + var result = await f.Sut.AuthorizeAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeFalse(); result.RequiresReauthentication.Should().BeFalse(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/RoleServiceTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/RoleServiceTests.cs index f8c0caae..d0b698d8 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/RoleServiceTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/RoleServiceTests.cs @@ -45,7 +45,7 @@ public async Task CreateAsync_WhenValid_CreatesAndPersistsRole() var result = await f.Sut.CreateAsync( context, " Administrators ", - permissions); + permissions, ct: TestContext.Current.CancellationToken); result.Should().BeSameAs(captured); @@ -144,7 +144,7 @@ public async Task RenameAsync_WhenValid_RenamesAndSavesWithOriginalVersion() await f.Sut.RenameAsync( context, role.Id, - " New Name "); + " New Name ", ct: TestContext.Current.CancellationToken); role.Name.Should().Be("New Name"); role.NormalizedName.Should().Be("NEW NAME"); @@ -224,7 +224,7 @@ public async Task DeleteAsync_WhenValid_RemovesAssignmentsAndUsesRequestedMode( var result = await f.Sut.DeleteAsync( context, role.Id, - mode); + mode, ct: TestContext.Current.CancellationToken); result.RoleId.Should().Be(role.Id); result.RemovedAssignments.Should().Be(13); @@ -322,7 +322,7 @@ public async Task SetPermissionsAsync_WhenValid_UpdatesAndSavesWithOriginalVersi await f.Sut.SetPermissionsAsync( context, role.Id, - permissions); + permissions, ct: TestContext.Current.CancellationToken); role.Permissions.Should().ContainSingle().Which.Should().Be(Permission.From("users.read")); @@ -376,7 +376,7 @@ public async Task QueryAsync_ForwardsQueryAndReturnsStoreResult() It.IsAny())) .ReturnsAsync(expected); - var result = await f.Sut.QueryAsync(context, query); + var result = await f.Sut.QueryAsync(context, query, ct: TestContext.Current.CancellationToken); result.Should().BeSameAs(expected); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/Store/RoleStoreContractTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/Store/RoleStoreContractTests.cs index b76b6146..484ee8ed 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/Store/RoleStoreContractTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/Store/RoleStoreContractTests.cs @@ -27,10 +27,10 @@ public async Task AddAsync_WhenValid_PersistsRole() var role = CreateRole("Administrators"); - await store.AddAsync(role); + await store.AddAsync(role, ct: TestContext.Current.CancellationToken); var result = await store.GetAsync( - new RoleKey(Tenant, role.Id)); + new RoleKey(Tenant, role.Id), ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.Id.Should().Be(role.Id); @@ -49,7 +49,7 @@ public async Task AddAsync_WhenActiveNormalizedNameAlreadyExists_ThrowsConflict( var first = CreateRole("Administrators"); var duplicate = CreateRole(" administrators "); - await store.AddAsync(first); + await store.AddAsync(first, ct: TestContext.Current.CancellationToken); var act = () => store.AddAsync(duplicate); @@ -65,13 +65,13 @@ public async Task AddAsync_WhenDeletedRoleHasSameNormalizedName_ThrowsConflict() var first = CreateRole("Administrators"); - await store.AddAsync(first); + await store.AddAsync(first, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new RoleKey(Tenant, first.Id), first.Version, DeleteMode.Soft, - Now); + Now, ct: TestContext.Current.CancellationToken); var replacement = CreateRole("Administrators"); @@ -89,13 +89,13 @@ public async Task AddAsync_WhenPreviousRoleWasHardDeleted_AllowsNameReuse() var first = CreateRole("Administrators"); - await store.AddAsync(first); + await store.AddAsync(first, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new RoleKey(Tenant, first.Id), first.Version, DeleteMode.Hard, - Now); + Now, ct: TestContext.Current.CancellationToken); var replacement = CreateRole("Administrators"); @@ -104,7 +104,7 @@ await store.DeleteAsync( await act.Should().NotThrowAsync(); var result = await store.GetByNameAsync( - replacement.NormalizedName); + replacement.NormalizedName, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.Id.Should().Be(replacement.Id); @@ -122,10 +122,10 @@ public async Task GetByNameAsync_WhenRoleExists_ReturnsRole() var role = CreateRole("Administrators"); - await store.AddAsync(role); + await store.AddAsync(role, ct: TestContext.Current.CancellationToken); var result = await store.GetByNameAsync( - role.NormalizedName); + role.NormalizedName, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.Id.Should().Be(role.Id); @@ -140,16 +140,16 @@ public async Task GetByNameAsync_WhenRoleIsDeleted_ReturnsNull() var role = CreateRole("Administrators"); - await store.AddAsync(role); + await store.AddAsync(role, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new RoleKey(Tenant, role.Id), role.Version, DeleteMode.Soft, - Now); + Now, ct: TestContext.Current.CancellationToken); var result = await store.GetByNameAsync( - role.NormalizedName); + role.NormalizedName, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -168,12 +168,12 @@ public async Task GetByIdsAsync_ReturnsOnlyRequestedRoles() var operatorRole = CreateRole("Operators"); var unrelated = CreateRole("Auditors"); - await store.AddAsync(admin); - await store.AddAsync(operatorRole); - await store.AddAsync(unrelated); + await store.AddAsync(admin, ct: TestContext.Current.CancellationToken); + await store.AddAsync(operatorRole, ct: TestContext.Current.CancellationToken); + await store.AddAsync(unrelated, ct: TestContext.Current.CancellationToken); var result = await store.GetByIdsAsync( - [admin.Id, operatorRole.Id]); + [admin.Id, operatorRole.Id], ct: TestContext.Current.CancellationToken); result.Select(x => x.Id) .Should() @@ -192,17 +192,17 @@ public async Task GetByIdsAsync_DoesNotReturnDeletedRoles() var active = CreateRole("Active"); var deleted = CreateRole("Deleted"); - await store.AddAsync(active); - await store.AddAsync(deleted); + await store.AddAsync(active, ct: TestContext.Current.CancellationToken); + await store.AddAsync(deleted, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new RoleKey(Tenant, deleted.Id), deleted.Version, DeleteMode.Soft, - Now); + Now, ct: TestContext.Current.CancellationToken); var result = await store.GetByIdsAsync( - [active.Id, deleted.Id]); + [active.Id, deleted.Id], ct: TestContext.Current.CancellationToken); result.Should().ContainSingle(); result.Single().Id.Should().Be(active.Id); @@ -220,10 +220,10 @@ public async Task SaveAsync_WhenRoleIsChanged_PersistsChanges() var role = CreateRole("Administrators"); - await store.AddAsync(role); + await store.AddAsync(role, ct: TestContext.Current.CancellationToken); var persisted = await store.GetAsync( - new RoleKey(Tenant, role.Id)); + new RoleKey(Tenant, role.Id), ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); @@ -233,10 +233,10 @@ public async Task SaveAsync_WhenRoleIsChanged_PersistsChanges() await store.SaveAsync( persisted, - expectedVersion); + expectedVersion, ct: TestContext.Current.CancellationToken); var result = await store.GetAsync( - new RoleKey(Tenant, role.Id)); + new RoleKey(Tenant, role.Id), ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.Name.Should().Be("Operators"); @@ -251,10 +251,10 @@ public async Task SaveAsync_WhenExpectedVersionIsStale_ThrowsConcurrency() var role = CreateRole("Administrators"); - await store.AddAsync(role); + await store.AddAsync(role, ct: TestContext.Current.CancellationToken); var persisted = await store.GetAsync( - new RoleKey(Tenant, role.Id)); + new RoleKey(Tenant, role.Id), ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); @@ -281,11 +281,11 @@ public async Task SaveAsync_WhenRenamedToExistingActiveRole_ThrowsConflict() var admin = CreateRole("Administrators"); var operators = CreateRole("Operators"); - await store.AddAsync(admin); - await store.AddAsync(operators); + await store.AddAsync(admin, ct: TestContext.Current.CancellationToken); + await store.AddAsync(operators, ct: TestContext.Current.CancellationToken); var persisted = await store.GetAsync( - new RoleKey(Tenant, operators.Id)); + new RoleKey(Tenant, operators.Id), ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); @@ -315,7 +315,7 @@ public async Task DeleteAsync_WhenSoftDeleted_KeepsRoleButMarksDeleted() var role = CreateRole("Administrators"); - await store.AddAsync(role); + await store.AddAsync(role, ct: TestContext.Current.CancellationToken); var deletedAt = Now.AddMinutes(1); @@ -323,10 +323,10 @@ await store.DeleteAsync( new RoleKey(Tenant, role.Id), role.Version, DeleteMode.Soft, - deletedAt); + deletedAt, ct: TestContext.Current.CancellationToken); var result = await store.GetAsync( - new RoleKey(Tenant, role.Id)); + new RoleKey(Tenant, role.Id), ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.IsDeleted.Should().BeTrue(); @@ -341,21 +341,21 @@ public async Task DeleteAsync_WhenHardDeleted_RemovesRole() var role = CreateRole("Administrators"); - await store.AddAsync(role); + await store.AddAsync(role, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new RoleKey(Tenant, role.Id), role.Version, DeleteMode.Hard, - Now); + Now, ct: TestContext.Current.CancellationToken); var result = await store.GetAsync( - new RoleKey(Tenant, role.Id)); + new RoleKey(Tenant, role.Id), ct: TestContext.Current.CancellationToken); result.Should().BeNull(); var exists = await store.ExistsAsync( - new RoleKey(Tenant, role.Id)); + new RoleKey(Tenant, role.Id), ct: TestContext.Current.CancellationToken); exists.Should().BeFalse(); } @@ -368,7 +368,7 @@ public async Task DeleteAsync_WhenExpectedVersionIsStale_ThrowsConcurrency() var role = CreateRole("Administrators"); - await store.AddAsync(role); + await store.AddAsync(role, ct: TestContext.Current.CancellationToken); var act = () => store.DeleteAsync( new RoleKey(Tenant, role.Id), @@ -393,20 +393,20 @@ public async Task QueryAsync_WhenIncludeDeletedIsFalse_ExcludesDeletedRoles() var active = CreateRole("Active"); var deleted = CreateRole("Deleted"); - await store.AddAsync(active); - await store.AddAsync(deleted); + await store.AddAsync(active, ct: TestContext.Current.CancellationToken); + await store.AddAsync(deleted, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new RoleKey(Tenant, deleted.Id), deleted.Version, DeleteMode.Soft, - Now); + Now, ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync( new RoleQuery { IncludeDeleted = false - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Should() .Contain(x => x.Id == active.Id); @@ -424,20 +424,20 @@ public async Task QueryAsync_WhenIncludeDeletedIsTrue_IncludesDeletedRoles() var active = CreateRole("Active"); var deleted = CreateRole("Deleted"); - await store.AddAsync(active); - await store.AddAsync(deleted); + await store.AddAsync(active, ct: TestContext.Current.CancellationToken); + await store.AddAsync(deleted, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new RoleKey(Tenant, deleted.Id), deleted.Version, DeleteMode.Soft, - Now); + Now, ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync( new RoleQuery { IncludeDeleted = true - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Select(x => x.Id) .Should() @@ -453,14 +453,14 @@ public async Task QueryAsync_WhenSearchSpecified_SearchesNormalizedName() var admin = CreateRole("Global Administrators"); var operators = CreateRole("Operators"); - await store.AddAsync(admin); - await store.AddAsync(operators); + await store.AddAsync(admin, ct: TestContext.Current.CancellationToken); + await store.AddAsync(operators, ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync( new RoleQuery { Search = " admin " - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Should().ContainSingle(); result.Items.Single().Id.Should().Be(admin.Id); @@ -481,7 +481,7 @@ public async Task QueryAsync_SortsBeforeApplyingPagination() "Delta" }) { - await store.AddAsync(CreateRole(name)); + await store.AddAsync(CreateRole(name), ct: TestContext.Current.CancellationToken); } var result = await store.QueryAsync( @@ -491,7 +491,7 @@ public async Task QueryAsync_SortsBeforeApplyingPagination() PageSize = 2, SortBy = nameof(Role.Name), Descending = false - }); + }, ct: TestContext.Current.CancellationToken); result.TotalCount.Should().Be(5); result.PageNumber.Should().Be(2); @@ -522,13 +522,13 @@ public async Task Store_IsTenantIsolated() "Administrators", tenantA); - await storeA.AddAsync(roleA); + await storeA.AddAsync(roleA, ct: TestContext.Current.CancellationToken); var fromA = await storeA.GetAsync( - new RoleKey(tenantA, roleA.Id)); + new RoleKey(tenantA, roleA.Id), ct: TestContext.Current.CancellationToken); var fromB = await storeB.GetAsync( - new RoleKey(tenantB, roleA.Id)); + new RoleKey(tenantB, roleA.Id), ct: TestContext.Current.CancellationToken); fromA.Should().NotBeNull(); fromB.Should().BeNull(); @@ -537,7 +537,7 @@ public async Task Store_IsTenantIsolated() new RoleQuery { IncludeDeleted = true - }); + }, ct: TestContext.Current.CancellationToken); queryB.Items.Should() .NotContain(x => x.Id == roleA.Id); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/Store/UserRoleStoreContractTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/Store/UserRoleStoreContractTests.cs index 1723e3bf..700b5327 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/Store/UserRoleStoreContractTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/Store/UserRoleStoreContractTests.cs @@ -30,9 +30,9 @@ public async Task AssignAsync_WhenValid_PersistsAssignment() var roleId = RoleId.New(); var assignedAt = Now.AddMinutes(-10); - await store.AssignAsync(user, roleId, assignedAt); + await store.AssignAsync(user, roleId, assignedAt, ct: TestContext.Current.CancellationToken); - var result = await store.GetAssignmentsAsync(user); + var result = await store.GetAssignmentsAsync(user, ct: TestContext.Current.CancellationToken); result.Should().ContainSingle(); @@ -53,7 +53,7 @@ public async Task AssignAsync_WhenSameRoleAlreadyAssigned_ThrowsConflict() var user = UserKey.New(); var roleId = RoleId.New(); - await store.AssignAsync(user, roleId, Now); + await store.AssignAsync(user, roleId, Now, ct: TestContext.Current.CancellationToken); var act = () => store.AssignAsync( user, @@ -75,10 +75,10 @@ public async Task AssignAsync_SameUserCanHaveMultipleRoles() var roleA = RoleId.New(); var roleB = RoleId.New(); - await store.AssignAsync(user, roleA, Now); - await store.AssignAsync(user, roleB, Now.AddMinutes(1)); + await store.AssignAsync(user, roleA, Now, ct: TestContext.Current.CancellationToken); + await store.AssignAsync(user, roleB, Now.AddMinutes(1), ct: TestContext.Current.CancellationToken); - var result = await store.GetAssignmentsAsync(user); + var result = await store.GetAssignmentsAsync(user, ct: TestContext.Current.CancellationToken); result.Should().HaveCount(2); @@ -97,11 +97,11 @@ public async Task AssignAsync_SameRoleCanBeAssignedToMultipleUsers() var userB = UserKey.New(); var roleId = RoleId.New(); - await store.AssignAsync(userA, roleId, Now); - await store.AssignAsync(userB, roleId, Now.AddMinutes(1)); + await store.AssignAsync(userA, roleId, Now, ct: TestContext.Current.CancellationToken); + await store.AssignAsync(userB, roleId, Now.AddMinutes(1), ct: TestContext.Current.CancellationToken); - var assignmentsA = await store.GetAssignmentsAsync(userA); - var assignmentsB = await store.GetAssignmentsAsync(userB); + var assignmentsA = await store.GetAssignmentsAsync(userA, ct: TestContext.Current.CancellationToken); + var assignmentsB = await store.GetAssignmentsAsync(userB, ct: TestContext.Current.CancellationToken); assignmentsA.Should().ContainSingle(); assignmentsB.Should().ContainSingle(); @@ -126,10 +126,10 @@ public async Task GetAssignmentsAsync_ReturnsOnlyRequestedUserAssignments() var roleA = RoleId.New(); var roleB = RoleId.New(); - await store.AssignAsync(userA, roleA, Now); - await store.AssignAsync(userB, roleB, Now); + await store.AssignAsync(userA, roleA, Now, ct: TestContext.Current.CancellationToken); + await store.AssignAsync(userB, roleB, Now, ct: TestContext.Current.CancellationToken); - var result = await store.GetAssignmentsAsync(userA); + var result = await store.GetAssignmentsAsync(userA, ct: TestContext.Current.CancellationToken); result.Should().ContainSingle(); result.Single().UserKey.Should().Be(userA); @@ -142,7 +142,7 @@ public async Task GetAssignmentsAsync_WhenUserHasNoAssignments_ReturnsEmpty() await using var db = await CreateDatabaseAsync(); var store = db.CreateStore(Tenant); - var result = await store.GetAssignmentsAsync(UserKey.New()); + var result = await store.GetAssignmentsAsync(UserKey.New(), ct: TestContext.Current.CancellationToken); result.Should().BeEmpty(); } @@ -162,12 +162,12 @@ public async Task RemoveAsync_WhenAssignmentExists_RemovesOnlyRequestedAssignmen var roleA = RoleId.New(); var roleB = RoleId.New(); - await store.AssignAsync(user, roleA, Now); - await store.AssignAsync(user, roleB, Now); + await store.AssignAsync(user, roleA, Now, ct: TestContext.Current.CancellationToken); + await store.AssignAsync(user, roleB, Now, ct: TestContext.Current.CancellationToken); - await store.RemoveAsync(user, roleA); + await store.RemoveAsync(user, roleA, ct: TestContext.Current.CancellationToken); - var result = await store.GetAssignmentsAsync(user); + var result = await store.GetAssignmentsAsync(user, ct: TestContext.Current.CancellationToken); result.Should().ContainSingle(); result.Single().RoleId.Should().Be(roleB); @@ -186,7 +186,7 @@ public async Task RemoveAsync_WhenAssignmentDoesNotExist_IsIdempotent() await act.Should().NotThrowAsync(); - var result = await store.GetAssignmentsAsync(user); + var result = await store.GetAssignmentsAsync(user, ct: TestContext.Current.CancellationToken); result.Should().BeEmpty(); } @@ -201,13 +201,13 @@ public async Task RemoveAsync_DoesNotRemoveSameRoleFromOtherUser() var userB = UserKey.New(); var roleId = RoleId.New(); - await store.AssignAsync(userA, roleId, Now); - await store.AssignAsync(userB, roleId, Now); + await store.AssignAsync(userA, roleId, Now, ct: TestContext.Current.CancellationToken); + await store.AssignAsync(userB, roleId, Now, ct: TestContext.Current.CancellationToken); - await store.RemoveAsync(userA, roleId); + await store.RemoveAsync(userA, roleId, ct: TestContext.Current.CancellationToken); - var assignmentsA = await store.GetAssignmentsAsync(userA); - var assignmentsB = await store.GetAssignmentsAsync(userB); + var assignmentsA = await store.GetAssignmentsAsync(userA, ct: TestContext.Current.CancellationToken); + var assignmentsB = await store.GetAssignmentsAsync(userB, ct: TestContext.Current.CancellationToken); assignmentsA.Should().BeEmpty(); @@ -231,14 +231,14 @@ public async Task RemoveAssignmentsByRoleAsync_RemovesRoleFromAllUsers() var targetRole = RoleId.New(); var otherRole = RoleId.New(); - await store.AssignAsync(userA, targetRole, Now); - await store.AssignAsync(userA, otherRole, Now); - await store.AssignAsync(userB, targetRole, Now); + await store.AssignAsync(userA, targetRole, Now, ct: TestContext.Current.CancellationToken); + await store.AssignAsync(userA, otherRole, Now, ct: TestContext.Current.CancellationToken); + await store.AssignAsync(userB, targetRole, Now, ct: TestContext.Current.CancellationToken); - await store.RemoveAssignmentsByRoleAsync(targetRole); + await store.RemoveAssignmentsByRoleAsync(targetRole, ct: TestContext.Current.CancellationToken); - var assignmentsA = await store.GetAssignmentsAsync(userA); - var assignmentsB = await store.GetAssignmentsAsync(userB); + var assignmentsA = await store.GetAssignmentsAsync(userA, ct: TestContext.Current.CancellationToken); + var assignmentsB = await store.GetAssignmentsAsync(userB, ct: TestContext.Current.CancellationToken); assignmentsA.Should().ContainSingle(); assignmentsA.Single().RoleId.Should().Be(otherRole); @@ -271,12 +271,12 @@ public async Task CountAssignmentsAsync_ReturnsNumberOfAssignmentsForRole() var roleA = RoleId.New(); var roleB = RoleId.New(); - await store.AssignAsync(UserKey.New(), roleA, Now); - await store.AssignAsync(UserKey.New(), roleA, Now); - await store.AssignAsync(UserKey.New(), roleA, Now); - await store.AssignAsync(UserKey.New(), roleB, Now); + await store.AssignAsync(UserKey.New(), roleA, Now, ct: TestContext.Current.CancellationToken); + await store.AssignAsync(UserKey.New(), roleA, Now, ct: TestContext.Current.CancellationToken); + await store.AssignAsync(UserKey.New(), roleA, Now, ct: TestContext.Current.CancellationToken); + await store.AssignAsync(UserKey.New(), roleB, Now, ct: TestContext.Current.CancellationToken); - var result = await store.CountAssignmentsAsync(roleA); + var result = await store.CountAssignmentsAsync(roleA, ct: TestContext.Current.CancellationToken); result.Should().Be(3); } @@ -288,7 +288,7 @@ public async Task CountAssignmentsAsync_WhenNoAssignmentsExist_ReturnsZero() var store = db.CreateStore(Tenant); var result = - await store.CountAssignmentsAsync(RoleId.New()); + await store.CountAssignmentsAsync(RoleId.New(), ct: TestContext.Current.CancellationToken); result.Should().Be(0); } @@ -311,10 +311,10 @@ public async Task GetAssignmentsAsync_IsTenantIsolated() var user = UserKey.New(); var roleId = RoleId.New(); - await storeA.AssignAsync(user, roleId, Now); + await storeA.AssignAsync(user, roleId, Now, ct: TestContext.Current.CancellationToken); - var fromA = await storeA.GetAssignmentsAsync(user); - var fromB = await storeB.GetAssignmentsAsync(user); + var fromA = await storeA.GetAssignmentsAsync(user, ct: TestContext.Current.CancellationToken); + var fromB = await storeB.GetAssignmentsAsync(user, ct: TestContext.Current.CancellationToken); fromA.Should().ContainSingle(); fromB.Should().BeEmpty(); @@ -333,13 +333,13 @@ public async Task CountAssignmentsAsync_IsTenantIsolated() var roleId = RoleId.New(); - await storeA.AssignAsync(UserKey.New(), roleId, Now); - await storeA.AssignAsync(UserKey.New(), roleId, Now); + await storeA.AssignAsync(UserKey.New(), roleId, Now, ct: TestContext.Current.CancellationToken); + await storeA.AssignAsync(UserKey.New(), roleId, Now, ct: TestContext.Current.CancellationToken); - await storeB.AssignAsync(UserKey.New(), roleId, Now); + await storeB.AssignAsync(UserKey.New(), roleId, Now, ct: TestContext.Current.CancellationToken); - var countA = await storeA.CountAssignmentsAsync(roleId); - var countB = await storeB.CountAssignmentsAsync(roleId); + var countA = await storeA.CountAssignmentsAsync(roleId, ct: TestContext.Current.CancellationToken); + var countB = await storeB.CountAssignmentsAsync(roleId, ct: TestContext.Current.CancellationToken); countA.Should().Be(2); countB.Should().Be(1); @@ -361,16 +361,16 @@ public async Task RemoveAssignmentsByRoleAsync_IsTenantIsolated() var userA = UserKey.New(); var userB = UserKey.New(); - await storeA.AssignAsync(userA, roleId, Now); - await storeB.AssignAsync(userB, roleId, Now); + await storeA.AssignAsync(userA, roleId, Now, ct: TestContext.Current.CancellationToken); + await storeB.AssignAsync(userB, roleId, Now, ct: TestContext.Current.CancellationToken); - await storeA.RemoveAssignmentsByRoleAsync(roleId); + await storeA.RemoveAssignmentsByRoleAsync(roleId, ct: TestContext.Current.CancellationToken); var assignmentsA = - await storeA.GetAssignmentsAsync(userA); + await storeA.GetAssignmentsAsync(userA, ct: TestContext.Current.CancellationToken); var assignmentsB = - await storeB.GetAssignmentsAsync(userB); + await storeB.GetAssignmentsAsync(userB, ct: TestContext.Current.CancellationToken); assignmentsA.Should().BeEmpty(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/UserPermissionServiceTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/UserPermissionServiceTests.cs index 39fd8b08..051c3e84 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/UserPermissionServiceTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/UserPermissionServiceTests.cs @@ -37,7 +37,7 @@ public async Task GetMyPermissionsAsync_ReturnsDistinctSortedPermissions() Permission.From("orders.update") ]); - var result = await f.Sut.GetMyPermissionsAsync(f.Context); + var result = await f.Sut.GetMyPermissionsAsync(f.Context, ct: TestContext.Current.CancellationToken); result.Permissions.Select(x => x.Value) .Should().Equal( @@ -65,7 +65,7 @@ public async Task GetMyPermissionsAsync_WhenNoPermissions_ReturnsEmpty() It.IsAny())) .ReturnsAsync(Array.Empty()); - var result = await f.Sut.GetMyPermissionsAsync(f.Context); + var result = await f.Sut.GetMyPermissionsAsync(f.Context, ct: TestContext.Current.CancellationToken); result.Permissions.Should().BeEmpty(); } @@ -82,7 +82,7 @@ public async Task GetMyPermissionsAsync_UsesResourceTenantAndActor() It.IsAny())) .ReturnsAsync(Array.Empty()); - await f.Sut.GetMyPermissionsAsync(f.Context); + await f.Sut.GetMyPermissionsAsync(f.Context, ct: TestContext.Current.CancellationToken); f.Permissions.Verify(x => x.GetPermissionsAsync( f.Context.ResourceTenant, diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/UserRoleServiceTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/UserRoleServiceTests.cs index b1ea60c5..dbc71e6a 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/UserRoleServiceTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Authorization/UserRoleServiceTests.cs @@ -49,7 +49,7 @@ public async Task AssignAsync_WhenRoleExists_NormalizesRoleNameAndAssignsTargetU await f.Sut.AssignAsync( context, target, - " administrators "); + " administrators ", ct: TestContext.Current.CancellationToken); f.RoleStore.Verify(x => x.GetByNameAsync( "ADMINISTRATORS", @@ -156,7 +156,7 @@ public async Task AssignAsync_UsesResourceTenantForBothStores() await f.Sut.AssignAsync( context, target, - "Admin"); + "Admin", ct: TestContext.Current.CancellationToken); f.RoleFactory.Verify( x => x.Create(context.ResourceTenant), @@ -196,7 +196,7 @@ public async Task RemoveAsync_WhenRoleExists_NormalizesRoleNameAndRemovesAssignm await f.Sut.RemoveAsync( context, target, - " operators "); + " operators ", ct: TestContext.Current.CancellationToken); f.UserRoleStore.Verify(x => x.RemoveAsync( target, @@ -259,7 +259,7 @@ public async Task RemoveAsync_WhenRoleIsDeleted_StillRemovesAssignment() await f.Sut.RemoveAsync( context, target, - "legacy"); + "legacy", ct: TestContext.Current.CancellationToken); f.UserRoleStore.Verify(x => x.RemoveAsync( target, @@ -309,7 +309,7 @@ public async Task GetRolesAsync_WhenAssignmentsExist_JoinsAssignmentsWithRoles() var result = await f.Sut.GetRolesAsync( context, target, - new PageRequest()); + new PageRequest(), ct: TestContext.Current.CancellationToken); result.TotalCount.Should().Be(2); result.Items.Should().HaveCount(2); @@ -373,7 +373,7 @@ public async Task GetRolesAsync_WhenAssignmentReferencesMissingRole_IgnoresOrpha var result = await f.Sut.GetRolesAsync( context, target, - new PageRequest()); + new PageRequest(), ct: TestContext.Current.CancellationToken); result.TotalCount.Should().Be(1); result.Items.Should().ContainSingle(); @@ -419,7 +419,7 @@ public async Task GetRolesAsync_AppliesPaginationAfterJoin() { PageNumber = 2, PageSize = 1 - }); + }, ct: TestContext.Current.CancellationToken); result.TotalCount.Should().Be(3); result.PageNumber.Should().Be(2); @@ -457,7 +457,7 @@ public async Task GetRolesAsync_NormalizesInvalidPagingValues() { PageNumber = -10, PageSize = 0 - }); + }, ct: TestContext.Current.CancellationToken); result.PageNumber.Should().Be(1); result.PageSize.Should().Be(250); @@ -491,7 +491,7 @@ public async Task GetRolesAsync_WhenPageSizeExceedsMaximum_ClampsPageSize() { PageNumber = 1, PageSize = 5000 - }); + }, ct: TestContext.Current.CancellationToken); result.PageSize.Should().Be(1000); } @@ -524,7 +524,7 @@ public async Task GetRolesAsync_PreservesPagingMetadata() PageSize = 20, SortBy = "name", Descending = true - }); + }, ct: TestContext.Current.CancellationToken); result.PageNumber.Should().Be(3); result.PageSize.Should().Be(20); @@ -554,7 +554,7 @@ public async Task GetRolesAsync_UsesResourceTenantForBothStores() await f.Sut.GetRolesAsync( context, target, - new PageRequest()); + new PageRequest(), ct: TestContext.Current.CancellationToken); f.RoleFactory.Verify( x => x.Create(context.ResourceTenant), @@ -620,7 +620,7 @@ public async Task GetRolesAsync_WhenSortByNameAscending_SortsByName() { SortBy = nameof(UserRoleInfo.Name), Descending = false - }); + }, ct: TestContext.Current.CancellationToken); result.Items .Select(x => x.Name) @@ -655,7 +655,7 @@ public async Task GetRolesAsync_WhenSortByNameDescending_SortsByNameDescending() { SortBy = nameof(UserRoleInfo.Name), Descending = true - }); + }, ct: TestContext.Current.CancellationToken); result.Items .Select(x => x.Name) @@ -694,7 +694,7 @@ public async Task GetRolesAsync_WhenSortByAssignedAtAscending_SortsByAssignedAt( { SortBy = nameof(UserRoleInfo.AssignedAt), Descending = false - }); + }, ct: TestContext.Current.CancellationToken); result.Items .Select(x => x.AssignedAt) @@ -733,7 +733,7 @@ public async Task GetRolesAsync_WhenSortByAssignedAtDescending_SortsByAssignedAt { SortBy = nameof(UserRoleInfo.AssignedAt), Descending = true - }); + }, ct: TestContext.Current.CancellationToken); result.Items .Select(x => x.AssignedAt) @@ -764,7 +764,7 @@ public async Task GetRolesAsync_WhenSortByIsNotSpecified_DefaultsToNameAscending var result = await f.Sut.GetRolesAsync( context, target, - new PageRequest()); + new PageRequest(), ct: TestContext.Current.CancellationToken); result.Items .Select(x => x.Name) @@ -816,7 +816,7 @@ public async Task GetRolesAsync_SortsBeforeApplyingPagination() PageNumber = 2, PageSize = 2, SortBy = nameof(UserRoleInfo.Name) - }); + }, ct: TestContext.Current.CancellationToken); result.TotalCount.Should().Be(5); result.PageNumber.Should().Be(2); @@ -874,7 +874,7 @@ public async Task GetRolesAsync_WhenNamesAreEqual_UsesRoleIdAsDeterministicTieBr new PageRequest { SortBy = nameof(UserRoleInfo.Name) - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Select(x => x.RoleId) .Should() diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/AuthStateSnapshotFactoryTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/AuthStateSnapshotFactoryTests.cs index 973c98fc..fa7af2e6 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/AuthStateSnapshotFactoryTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/AuthStateSnapshotFactoryTests.cs @@ -21,7 +21,7 @@ public async Task CreateAsync_should_return_snapshot_when_valid() var pprovider = new Mock(); var lprovider = new Mock(); - provider.Setup(x => x.GetAsync(It.IsAny(), It.IsAny(), default)) + provider.Setup(x => x.GetAsync(It.IsAny(), It.IsAny(), TestContext.Current.CancellationToken)) .ReturnsAsync(new PrimaryUserIdentifiers { UserName = "admin" @@ -39,7 +39,7 @@ public async Task CreateAsync_should_return_snapshot_when_valid() DateTimeOffset.UtcNow ); - var snapshot = await factory.CreateAsync(validation); + var snapshot = await factory.CreateAsync(validation, ct: TestContext.Current.CancellationToken); snapshot.Should().NotBeNull(); snapshot!.Identity.PrimaryUserName.Should().Be("admin"); @@ -55,7 +55,7 @@ public async Task CreateAsync_should_return_null_when_invalid() var factory = new AuthStateSnapshotFactory(provider.Object, pprovider.Object, lprovider.Object); var validation = SessionValidationResult.Invalid(SessionState.NotFound); - var snapshot = await factory.CreateAsync(validation); + var snapshot = await factory.CreateAsync(validation, ct: TestContext.Current.CancellationToken); snapshot.Should().BeNull(); } } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/Blazor/UAuthRequestClientTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/Blazor/UAuthRequestClientTests.cs index 821df048..e6148acd 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/Blazor/UAuthRequestClientTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/Blazor/UAuthRequestClientTests.cs @@ -41,7 +41,7 @@ private UAuthRequestClient CreateSut(UAuthClientProfile profile = UAuthClientPro public async Task NavigateAsync_InvokesUAuthPost() { var sut = CreateSut(); - await sut.NavigateAsync("/login"); + await sut.NavigateAsync("/login", ct: Xunit.TestContext.Current.CancellationToken); var invocation = JSInterop.Invocations.Single(x => x.Identifier == "uauth.post"); invocation.Arguments.Should().ContainSingle(); @@ -61,7 +61,7 @@ public async Task SendFormAsync_ReturnsTransportResult() .SetResult(expected); var sut = CreateSut(); - var result = await sut.SendFormAsync("/login"); + var result = await sut.SendFormAsync("/login", ct: Xunit.TestContext.Current.CancellationToken); result.Should().BeSameAs(expected); } @@ -153,7 +153,7 @@ public async Task TryAndCommitThrottleKeepsStatusAndRetryGuidance() JSInterop.Setup("uauth.tryAndCommit", _ => true) .SetResult(System.Text.Json.JsonSerializer.SerializeToElement(new { status = 429, retryAfter = "45", isSuccess = false })); var error = await Assert.ThrowsAsync(() => - CreateSut().TryAndCommitAsync("/try", "/commit", new { })); + CreateSut().TryAndCommitAsync("/try", "/commit", new { }, ct: Xunit.TestContext.Current.CancellationToken)); Assert.Equal(System.Net.HttpStatusCode.TooManyRequests, error.StatusCode); Assert.Equal("rate_limited", error.Code); Assert.Equal(TimeSpan.FromSeconds(45), error.RetryAfter); @@ -164,7 +164,7 @@ public async Task TryAndCommitStillDeserializesSuccessfulPreview() { JSInterop.Setup("uauth.tryAndCommit", _ => true) .SetResult(System.Text.Json.JsonSerializer.SerializeToElement(new { isSuccess = true, previewReceipt = "receipt" })); - var result = await CreateSut().TryAndCommitAsync("/try", "/commit", new { }); + var result = await CreateSut().TryAndCommitAsync("/try", "/commit", new { }, ct: Xunit.TestContext.Current.CancellationToken); Assert.True(result.IsSuccess); Assert.Equal("receipt", result.PreviewReceipt); } @@ -174,7 +174,7 @@ public async Task ObservedLogoutReturnsHttpRejectionRatherThanSubmittingAnUnobse { JSInterop.Setup("uauth.post", _ => true) .SetResult(new UAuthTransportResult { Status = 429, RetryAfter = "15" }); - var result = await CreateSut().SendFormAsync("/auth/logout", null, navigateOnSuccess: true); + var result = await CreateSut().SendFormAsync("/auth/logout", null, navigateOnSuccess: true, ct: Xunit.TestContext.Current.CancellationToken); Assert.Equal(429, result.Status); Assert.Equal("15", result.RetryAfter); var invocation = JSInterop.Invocations.Single(x => x.Identifier == "uauth.post"); @@ -196,7 +196,7 @@ public async Task SendFormAsync_ResolvesTrustedOriginCorrectly(string basePath, var sut = CreateSut(basePath: basePath); // Act - await sut.SendFormAsync("/auth/validate"); + await sut.SendFormAsync("/auth/validate", ct: Xunit.TestContext.Current.CancellationToken); // Assert var invocation = JSInterop.Invocations diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/ClientRateLimitingTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/ClientRateLimitingTests.cs index e3ee0872..527a1367 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/ClientRateLimitingTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/ClientRateLimitingTests.cs @@ -64,16 +64,16 @@ public async Task ValidationThrottlePreservesStateAndBlazorPrincipal_ThenRevocat .ReturnsAsync(new UAuthTransportResult { Status = 200, Ok = true, Body = JsonSerializer.SerializeToElement(new AuthValidationResult { State = SessionState.Revoked }) }); using var manager = new UAuthStateManager(CreateClient(flows: CreateFlowClient()), Events.Object, clock); var provider = new UAuthAuthenticationStateProvider(manager); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); var lastValidated = manager.State.LastValidatedAt; - await manager.EnsureAsync(force: true); - await manager.EnsureAsync(force: true); + await manager.EnsureAsync(force: true, ct: TestContext.Current.CancellationToken); + await manager.EnsureAsync(force: true, ct: TestContext.Current.CancellationToken); Assert.True(manager.State.IsAuthenticated); Assert.True((await provider.GetAuthenticationStateAsync()).User.Identity!.IsAuthenticated); Assert.Equal(lastValidated, manager.State.LastValidatedAt); Request.Verify(x => x.SendFormAsync("/auth/validate", null, It.IsAny()), Times.Exactly(2)); clock.Advance(TimeSpan.FromMinutes(2)); - await manager.EnsureAsync(force: true); + await manager.EnsureAsync(force: true, ct: TestContext.Current.CancellationToken); Assert.False(manager.State.IsAuthenticated); Assert.False((await provider.GetAuthenticationStateAsync()).User.Identity!.IsAuthenticated); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/SessionCoordinatorTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/SessionCoordinatorTests.cs index c3338855..d4924138 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/SessionCoordinatorTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/SessionCoordinatorTests.cs @@ -30,7 +30,7 @@ public async Task StartAsync_should_not_start_when_auto_refresh_disabled() }); var coordinator = new SessionCoordinator(client.Object, nav.Object, options, diagnostics, clock); - await coordinator.StartAsync(); + await coordinator.StartAsync(cancellationToken: TestContext.Current.CancellationToken); Assert.False(diagnostics.IsRunning); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/UAuthClientBootstrapperTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/UAuthClientBootstrapperTests.cs index ce53fef8..68151e9e 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/UAuthClientBootstrapperTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/UAuthClientBootstrapperTests.cs @@ -23,8 +23,8 @@ public async Task EnsureStartedAsync_should_initialize_only_once() browser, new UAuthClientOptions()); - await bootstrapper.EnsureStartedAsync(); - await bootstrapper.EnsureStartedAsync(); + await bootstrapper.EnsureStartedAsync(ct: TestContext.Current.CancellationToken); + await bootstrapper.EnsureStartedAsync(ct: TestContext.Current.CancellationToken); bootstrapper.IsStarted.Should().BeTrue(); @@ -100,7 +100,7 @@ public async Task EnsureStartedAsync_should_configure_same_origin_antiforgery() browser, options); - await bootstrapper.EnsureStartedAsync(); + await bootstrapper.EnsureStartedAsync(ct: TestContext.Current.CancellationToken); browser.Verify( x => x.ConfigureAntiforgeryAsync( @@ -131,7 +131,7 @@ public async Task EnsureStartedAsync_should_configure_cross_origin_antiforgery() browser, options); - await bootstrapper.EnsureStartedAsync(); + await bootstrapper.EnsureStartedAsync(ct: TestContext.Current.CancellationToken); browser.Verify( x => x.ConfigureAntiforgeryAsync( @@ -175,11 +175,11 @@ public async Task EnsureStartedAsync_should_retry_when_antiforgery_configuration new UAuthClientOptions()); await Assert.ThrowsAsync( - () => bootstrapper.EnsureStartedAsync()); + () => bootstrapper.EnsureStartedAsync(ct: TestContext.Current.CancellationToken)); bootstrapper.IsStarted.Should().BeFalse(); - await bootstrapper.EnsureStartedAsync(); + await bootstrapper.EnsureStartedAsync(ct: TestContext.Current.CancellationToken); bootstrapper.IsStarted.Should().BeTrue(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/UAuthResourceClientTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/UAuthResourceClientTests.cs index 781e1b8f..e9680496 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/UAuthResourceClientTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/UAuthResourceClientTests.cs @@ -40,19 +40,19 @@ public async Task Resource_Methods_Should_Forward_Correct_Method(string method) switch (method) { case "GET": - await client.GetAsync("/api/products"); + await client.GetAsync("/api/products", ct: TestContext.Current.CancellationToken); break; case "POST": - await client.PostAsync("/api/products", payload); + await client.PostAsync("/api/products", payload, ct: TestContext.Current.CancellationToken); break; case "PUT": - await client.PutAsync("/api/products", payload); + await client.PutAsync("/api/products", payload, ct: TestContext.Current.CancellationToken); break; case "PATCH": - await client.PatchAsync("/api/products", payload); + await client.PatchAsync("/api/products", payload, ct: TestContext.Current.CancellationToken); break; } @@ -82,7 +82,7 @@ public async Task Get_Should_Deserialize_Success_Response() }); var result = await CreateClient() - .GetAsync("/api/products/1"); + .GetAsync("/api/products/1", ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); result.Status.Should().Be(200); @@ -108,7 +108,7 @@ public async Task Get_Should_Return_Default_When_Body_Is_Empty() }); var result = await CreateClient() - .GetAsync("/api/products/1"); + .GetAsync("/api/products/1", ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); result.Status.Should().Be(204); @@ -138,7 +138,7 @@ public async Task Get_Should_Parse_Problem_On_Failure() }); var result = await CreateClient() - .GetAsync("/api/products"); + .GetAsync("/api/products", ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); result.Status.Should().Be(403); @@ -164,7 +164,7 @@ public async Task Get_Should_Handle_Invalid_Problem_Json() }); var result = await CreateClient() - .GetAsync("/api/products"); + .GetAsync("/api/products", ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); result.Status.Should().Be(500); @@ -187,7 +187,7 @@ public async Task Delete_Should_Return_Success_Without_Body() }); var result = await CreateClient() - .DeleteAsync("/api/products/1"); + .DeleteAsync("/api/products/1", ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); result.Status.Should().Be(204); @@ -211,7 +211,7 @@ public async Task Delete_Should_Parse_Problem_On_Failure() }); var result = await CreateClient() - .DeleteAsync("/api/products/1"); + .DeleteAsync("/api/products/1", ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); result.Status.Should().Be(404); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/UAuthResourceRequestClientTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/UAuthResourceRequestClientTests.cs index 06c5ede3..99c7c126 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/UAuthResourceRequestClientTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/UAuthResourceRequestClientTests.cs @@ -65,7 +65,7 @@ public async Task Send_Should_Ensure_Bootstrap_Before_JS_Request() var client = CreateClient(); - var task = client.SendAsync("GET", "/api/products"); + var task = client.SendAsync("GET", "/api/products", ct: TestContext.Current.CancellationToken); _bootstrapper.Verify(x => x.EnsureStartedAsync( It.IsAny()), Times.Once); @@ -111,7 +111,7 @@ public async Task Send_Should_Pass_Correct_Arguments_To_JS() await client.SendAsync( "POST", "/api/products", - payload); + payload, ct: TestContext.Current.CancellationToken); capturedOptions.Should().NotBeNull(); @@ -159,7 +159,7 @@ public async Task Send_Should_Return_Success_Response() SetupResponse(response); var result = await CreateClient() - .SendAsync("GET", "/api/products/1"); + .SendAsync("GET", "/api/products/1", ct: TestContext.Current.CancellationToken); result.Should().BeSameAs(response); } @@ -177,7 +177,7 @@ public async Task Send_Should_Return_Http_Error_Response() SetupResponse(response); var result = await CreateClient() - .SendAsync("GET", "/api/products"); + .SendAsync("GET", "/api/products", ct: TestContext.Current.CancellationToken); result.Should().BeSameAs(response); result.Status.Should().Be(403); @@ -332,8 +332,8 @@ public async Task Send_Should_Generate_Unique_RequestIds() var client = CreateClient(); - await client.SendAsync("GET", "/api/products/1"); - await client.SendAsync("GET", "/api/products/2"); + await client.SendAsync("GET", "/api/products/1", ct: TestContext.Current.CancellationToken); + await client.SendAsync("GET", "/api/products/2", ct: TestContext.Current.CancellationToken); requestIds.Should().HaveCount(2); requestIds.Distinct().Should().HaveCount(2); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/UAuthStateManagerTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/UAuthStateManagerTests.cs index 8f25e4a3..5e1bc430 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/UAuthStateManagerTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Client/UAuthStateManagerTests.cs @@ -45,8 +45,8 @@ public async Task EnsureAsync_should_not_validate_when_authenticated_and_not_sta var manager = new UAuthStateManager(client.Object, events.Object, clock.Object); - await manager.EnsureAsync(); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); flowClient.Verify(x => x.ValidateAsync(), Times.Once); } @@ -70,9 +70,9 @@ public async Task EnsureAsync_Should_Deduplicate_InFlight_Calls_But_Allow_Later_ Snapshot = CreateSnapshot() }); - var first = manager.EnsureAsync(force: true); - var second = manager.EnsureAsync(force: true); - var third = manager.EnsureAsync(force: true); + var first = manager.EnsureAsync(force: true, ct: TestContext.Current.CancellationToken); + var second = manager.EnsureAsync(force: true, ct: TestContext.Current.CancellationToken); + var third = manager.EnsureAsync(force: true, ct: TestContext.Current.CancellationToken); first.IsCompleted.Should().BeFalse(); @@ -93,7 +93,7 @@ public async Task EnsureAsync_Should_Deduplicate_InFlight_Calls_But_Allow_Later_ manager.State.IsAuthenticated.Should().BeTrue(); - await manager.EnsureAsync(force: true); + await manager.EnsureAsync(force: true, ct: TestContext.Current.CancellationToken); flows.Verify( x => x.ValidateAsync(), @@ -115,7 +115,7 @@ public async Task EnsureAsync_invalid_should_clear_state() }); var manager = new UAuthStateManager(client.Object, events.Object, clock.Object); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); manager.State.IsAuthenticated.Should().BeFalse(); } @@ -127,7 +127,7 @@ public async Task OnLogin_Should_Mark_Authenticated_State_Stale() { SetupValidSnapshot(flows); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); manager.State.IsAuthenticated.Should().BeTrue(); manager.State.IsStale.Should().BeFalse(); @@ -160,7 +160,7 @@ public async Task OnLogout_Should_Clear_Authenticated_State() { SetupValidSnapshot(flows); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); await manager.OnLogoutAsync(); manager.State.IsAuthenticated.Should().BeFalse(); @@ -178,11 +178,11 @@ public async Task MarkStale_Should_Cause_Next_Ensure_To_Validate() { SetupValidSnapshot(flows); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); manager.MarkStale(); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); flows.Verify(x => x.ValidateAsync(), Times.Exactly(2)); manager.State.IsStale.Should().BeFalse(); @@ -197,14 +197,14 @@ public async Task Clear_Should_Require_Next_Validation() { SetupValidSnapshot(flows); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); manager.Clear(); manager.State.IsAuthenticated.Should().BeFalse(); manager.NeedsValidation.Should().BeTrue(); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); flows.Verify(x => x.ValidateAsync(), Times.Exactly(2)); } @@ -222,7 +222,7 @@ public async Task State_Invalidating_Events_Should_Clear_State(UAuthStateEvent e { SetupValidSnapshot(flows); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); await events.PublishAsync( new UAuthStateEventArgsEmpty( @@ -242,7 +242,7 @@ public async Task ProfileChanged_Should_Patch_Current_Identity() { SetupValidSnapshot(flows); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); await events.PublishAsync( new UAuthStateEventArgs( @@ -268,7 +268,7 @@ public async Task UserStatusChanged_Should_Patch_Current_Identity() { SetupValidSnapshot(flows); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); await events.PublishAsync( new UAuthStateEventArgs( @@ -294,7 +294,7 @@ public async Task RefreshMode_None_Should_Ignore_Event() { SetupValidSnapshot(flows); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); await events.PublishAsync( new UAuthStateEventArgsEmpty( @@ -314,7 +314,7 @@ public async Task RefreshMode_Validate_Should_Force_Validation() { SetupValidSnapshot(flows); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); await events.PublishAsync( new UAuthStateEventArgsEmpty( @@ -333,7 +333,7 @@ public async Task ValidationCalled_With_Patch_Should_Mark_Validated() { SetupValidSnapshot(flows); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); manager.MarkStale(); @@ -362,7 +362,7 @@ public async Task AuthorizationChanged_With_Patch_Should_Mark_Stale() { SetupValidSnapshot(flows); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); await events.PublishAsync( new UAuthStateEventArgsEmpty( @@ -381,7 +381,7 @@ public async Task Dispose_Should_Unsubscribe_From_State_Events() SetupValidSnapshot(flows); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); manager.Dispose(); @@ -401,9 +401,9 @@ public async Task EnsureAsync_Should_Allow_Revalidation_After_Synchronous_Comple using (manager) { SetupValidSnapshot(flows); - await manager.EnsureAsync(force: true); - await manager.EnsureAsync(force: true); - await manager.EnsureAsync(force: true); + await manager.EnsureAsync(force: true, ct: TestContext.Current.CancellationToken); + await manager.EnsureAsync(force: true, ct: TestContext.Current.CancellationToken); + await manager.EnsureAsync(force: true, ct: TestContext.Current.CancellationToken); flows.Verify(x => x.ValidateAsync(), Times.Exactly(3)); } } @@ -424,7 +424,7 @@ public async Task EnsureAsync_When_Session_Is_Not_Active_Should_Clear_State(Sess { SetupValidSnapshot(flows); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); manager.State.IsAuthenticated.Should().BeTrue(); @@ -434,7 +434,7 @@ public async Task EnsureAsync_When_Session_Is_Not_Active_Should_Clear_State(Sess State = sessionState }); - await manager.EnsureAsync(force: true); + await manager.EnsureAsync(force: true, ct: TestContext.Current.CancellationToken); manager.State.IsAuthenticated.Should().BeFalse(); manager.State.Identity.Should().BeNull(); @@ -471,7 +471,7 @@ await act.Should() manager.State.IsAuthenticated.Should().BeFalse(); - await manager.EnsureAsync(force: true); + await manager.EnsureAsync(force: true, ct: TestContext.Current.CancellationToken); manager.State.IsAuthenticated.Should().BeTrue(); manager.State.IsStale.Should().BeFalse(); @@ -491,7 +491,7 @@ public async Task EnsureAsync_When_Network_Fails_Should_Preserve_Authenticated_S { SetupValidSnapshot(flows); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); var originalIdentity = manager.State.Identity; var originalValidatedAt = manager.State.LastValidatedAt; @@ -520,7 +520,7 @@ public async Task EnsureAsync_When_Active_Result_Has_No_Snapshot_Should_Mark_Sta { SetupValidSnapshot(flows); - await manager.EnsureAsync(); + await manager.EnsureAsync(ct: TestContext.Current.CancellationToken); flows.Setup(x => x.ValidateAsync()) .ReturnsAsync(new AuthValidationResult @@ -529,7 +529,7 @@ public async Task EnsureAsync_When_Active_Result_Has_No_Snapshot_Should_Mark_Sta Snapshot = null }); - await manager.EnsureAsync(force: true); + await manager.EnsureAsync(force: true, ct: TestContext.Current.CancellationToken); manager.State.IsAuthenticated.Should().BeTrue(); manager.State.IsStale.Should().BeTrue(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/CodeBeam.UltimateAuth.Tests.Unit.csproj b/tests/CodeBeam.UltimateAuth.Tests.Unit/CodeBeam.UltimateAuth.Tests.Unit.csproj index d90a6ba2..bf71f5b0 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/CodeBeam.UltimateAuth.Tests.Unit.csproj +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/CodeBeam.UltimateAuth.Tests.Unit.csproj @@ -1,28 +1,18 @@  - net10.0 enable enable false - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - + - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - + + + + diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Core/RefreshTokenValidatorTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Core/RefreshTokenValidatorTests.cs index 4ce16d91..b506366e 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Core/RefreshTokenValidatorTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Core/RefreshTokenValidatorTests.cs @@ -38,7 +38,7 @@ public async Task Invalid_When_Token_Not_Found() RefreshToken = "non-existing", Now = DateTimeOffset.UtcNow, Device = DeviceContext.Create(DeviceId.Create(ValidDeviceId), null, null, null, null, null), - }); + }, ct: TestContext.Current.CancellationToken); Assert.False(result.IsValid); Assert.Equal(RefreshTokenValidationState.NotFound, result.State); @@ -69,7 +69,7 @@ public async Task Invalid_When_Token_Is_Revoked_Without_Replacement() now.AddMinutes(5)); await store.StoreAsync( - token.Revoke(now)); + token.Revoke(now), ct: TestContext.Current.CancellationToken); var result = await validator.ValidateAsync( new RefreshTokenValidationContext @@ -84,7 +84,7 @@ await store.StoreAsync( null, null, null) - }); + }, ct: TestContext.Current.CancellationToken); Assert.False(result.IsValid); Assert.Equal( @@ -119,7 +119,7 @@ public async Task Consumed_When_Token_Was_Replaced_By_Rotation() await store.StoreAsync( token.Revoke( now, - "replacement-refresh-token-hash")); + "replacement-refresh-token-hash"), ct: TestContext.Current.CancellationToken); var result = await validator.ValidateAsync( new RefreshTokenValidationContext @@ -134,7 +134,7 @@ await store.StoreAsync( null, null, null) - }); + }, ct: TestContext.Current.CancellationToken); Assert.False(result.IsValid); Assert.Equal( @@ -180,7 +180,7 @@ public async Task Invalid_When_Expected_Session_Id_Does_Not_Match() now, now.AddMinutes(10)); - await store.StoreAsync(token); + await store.StoreAsync(token, ct: TestContext.Current.CancellationToken); var result = await validator.ValidateAsync( new RefreshTokenValidationContext @@ -198,7 +198,7 @@ public async Task Invalid_When_Expected_Session_Id_Does_Not_Match() null, null, null), - }); + }, ct: TestContext.Current.CancellationToken); Assert.False(result.IsValid); @@ -238,7 +238,7 @@ public async Task Invalid_When_Token_Is_Expired() now.AddMinutes(-10), now.AddMinutes(-1)); - await store.StoreAsync(token); + await store.StoreAsync(token, ct: TestContext.Current.CancellationToken); var result = await validator.ValidateAsync( new RefreshTokenValidationContext @@ -253,7 +253,7 @@ public async Task Invalid_When_Token_Is_Expired() null, null, null), - }); + }, ct: TestContext.Current.CancellationToken); Assert.False(result.IsValid); @@ -286,7 +286,7 @@ public async Task Valid_When_Token_Is_Active() now, now.AddMinutes(10)); - await store.StoreAsync(token); + await store.StoreAsync(token, ct: TestContext.Current.CancellationToken); var result = await validator.ValidateAsync( new RefreshTokenValidationContext @@ -295,7 +295,7 @@ public async Task Valid_When_Token_Is_Active() RefreshToken = raw, Now = now, Device = DeviceContext.Create(DeviceId.Create(ValidDeviceId), null, null, null, null, null), - }); + }, ct: TestContext.Current.CancellationToken); Assert.True(result.IsValid); Assert.Equal(RefreshTokenValidationState.Valid, result.State); @@ -325,7 +325,7 @@ public async Task Reuse_Detected_When_Old_Token_Is_Reused_After_Rotation() now, now.AddMinutes(10)); - await store.StoreAsync(token.Revoke(now, "new-hash")); + await store.StoreAsync(token.Revoke(now, "new-hash"), ct: TestContext.Current.CancellationToken); var result = await validator.ValidateAsync( new RefreshTokenValidationContext @@ -334,7 +334,7 @@ public async Task Reuse_Detected_When_Old_Token_Is_Reused_After_Rotation() RefreshToken = raw, Now = now, Device = DeviceContext.Create(DeviceId.Create(ValidDeviceId), null, null, null, null, null), - }); + }, ct: TestContext.Current.CancellationToken); Assert.False(result.IsValid); Assert.Equal(RefreshTokenValidationState.Consumed, result.State); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/ChangePasswordTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/ChangePasswordTests.cs index 1af5ece3..4609b239 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/ChangePasswordTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/ChangePasswordTests.cs @@ -24,7 +24,7 @@ public async Task Change_password_with_correct_current_should_succeed() { CurrentSecret = "user", NewSecret = "newpass123" - }); + }, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); } @@ -75,7 +75,7 @@ public async Task Change_password_should_increment_version() var context = TestAccessContext.ForUser(TestUsers.User, UAuthActions.Credentials.ChangeSelf); - var before = await service.GetAllAsync(context); + var before = await service.GetAllAsync(context, ct: TestContext.Current.CancellationToken); var versionBefore = before.Credentials.Single().Version; await service.ChangeSecretAsync(context, @@ -83,9 +83,9 @@ await service.ChangeSecretAsync(context, { CurrentSecret = "user", NewSecret = "newpass123" - }); + }, ct: TestContext.Current.CancellationToken); - var after = await service.GetAllAsync(context); + var after = await service.GetAllAsync(context, ct: TestContext.Current.CancellationToken); var versionAfter = after.Credentials.Single().Version; versionAfter.Should().Be(versionBefore + 1); @@ -106,14 +106,14 @@ await service.ChangeSecretAsync(context, { CurrentSecret = "user", NewSecret = "newpass123" - }); + }, ct: TestContext.Current.CancellationToken); var oldLogin = await orchestrator.LoginAsync(flow, new LoginRequest { Identifier = "user", Secret = "user" - }); + }, ct: TestContext.Current.CancellationToken); oldLogin.IsSuccess.Should().BeFalse(); @@ -122,7 +122,7 @@ await service.ChangeSecretAsync(context, { Identifier = "user", Secret = "newpass123" - }); + }, ct: TestContext.Current.CancellationToken); newLogin.IsSuccess.Should().BeTrue(); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/CredentialManagementServiceTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/CredentialManagementServiceTests.cs index 4c9850f2..d4ffdc60 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/CredentialManagementServiceTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/CredentialManagementServiceTests.cs @@ -54,7 +54,7 @@ public async Task GetAllAsync_ReturnsCredentialsForTargetUser() It.IsAny())) .ReturnsAsync(new[] { credential }); - var result = await f.Sut.GetAllAsync(context); + var result = await f.Sut.GetAllAsync(context, ct: TestContext.Current.CancellationToken); result.Credentials.Should().ContainSingle(); @@ -98,7 +98,7 @@ public async Task AddAsync_HashesSecretAndPersistsCredentialForTargetUser() new AddCredentialRequest { Secret = "new-password" - }); + }, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); @@ -341,7 +341,7 @@ public async Task ChangeSecretAsync_Self_WhenValid_SavesAndRevokesOtherChains() { CurrentSecret = "current-password", NewSecret = "new-password" - }); + }, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); @@ -387,7 +387,7 @@ public async Task ChangeSecretAsync_SelfWithoutActorChain_RevokesAllChains() { CurrentSecret = "current-password", NewSecret = "new-password" - }); + }, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); @@ -455,7 +455,7 @@ public async Task ChangeSecretAsync_Admin_WhenValid_DoesNotRequireCurrentSecret_ { CurrentSecret = null, NewSecret = "new-password" - }); + }, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); @@ -520,7 +520,7 @@ public async Task RevokeAsync_WhenCredentialBelongsToDifferentUser_ReturnsNotFou new RevokeCredentialRequest { Id = id - }); + }, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); result.Error.Should().Be("credential_not_found"); @@ -570,7 +570,7 @@ public async Task RevokeAsync_WhenValid_RevokesAndSavesExpectedVersion() new RevokeCredentialRequest { Id = id - }); + }, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); } @@ -626,7 +626,7 @@ public async Task DeleteAsync_WhenValid_UsesRequestedDeleteMode(DeleteMode mode) { Id = id, Mode = mode - }); + }, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue( $"delete should succeed but returned '{result.Error}'"); @@ -673,7 +673,7 @@ public async Task DeleteAsync_WhenCredentialBelongsToDifferentUser_ReturnsNotFou { Id = id, Mode = DeleteMode.Hard - }); + }, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); result.Error.Should().Be("credential_not_found"); @@ -833,7 +833,7 @@ public async Task BeginResetAsync_Anonymous_UnknownIdentifier_DoesNotRequireTarg Identifier = identifier, CredentialType = CredentialType.Password, ResetCodeType = ResetCodeType.Token - }); + }, ct: TestContext.Current.CancellationToken); result.ExpiresAt.Should().BeAfter(Now); @@ -876,7 +876,7 @@ public async Task CompleteResetAsync_Anonymous_UnknownIdentifier_DoesNotRequireT CredentialType = CredentialType.Password, ResetToken = "some-token", NewSecret = "new-password" - }); + }, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); @@ -1053,7 +1053,7 @@ public async Task DeleteAsync_Hard_RemovesPasswordHistory() { Id = id, Mode = DeleteMode.Hard - }); + }, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); @@ -1105,7 +1105,7 @@ await f.Sut.AddAsync( new AddCredentialRequest { Secret = "ValidPassword123!" - }); + }, ct: TestContext.Current.CancellationToken); f.PasswordPipeline.Verify( x => x.ValidateAsync( diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/EfCorePasswordHistoryAtomicityTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/EfCorePasswordHistoryAtomicityTests.cs index 9f5a7626..4fa079f5 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/EfCorePasswordHistoryAtomicityTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/EfCorePasswordHistoryAtomicityTests.cs @@ -41,7 +41,7 @@ public async Task FailedHistoryOperation_RollsBackCredentialAndHistory( await using var connection = new SqliteConnection("Data Source=:memory:"); - await connection.OpenAsync(); + await connection.OpenAsync(cancellationToken: TestContext.Current.CancellationToken); var dbOptions = new DbContextOptionsBuilder() .UseSqlite(connection) @@ -52,7 +52,7 @@ public async Task FailedHistoryOperation_RollsBackCredentialAndHistory( await using (var db = new UAuthCredentialDbContext(dbOptions)) { - await db.Database.EnsureCreatedAsync(); + await db.Database.EnsureCreatedAsync(cancellationToken: TestContext.Current.CancellationToken); var user = UserKey.New(); @@ -73,7 +73,7 @@ public async Task FailedHistoryOperation_RollsBackCredentialAndHistory( var history = new EfCorePasswordHistoryStore( db, Tenant); - await credentials.AddAsync(credential); + await credentials.AddAsync(credential, ct: TestContext.Current.CancellationToken); originalHistoryId = Guid.NewGuid(); @@ -85,7 +85,7 @@ await history.AddAsync(new PasswordHistoryEntry CredentialKey = new CredentialKey(Tenant, credentialId), SecretHash = Hash("older"), ReplacedAt = Now.AddDays(-1) - }); + }, ct: TestContext.Current.CancellationToken); var pipeline = new Mock(); pipeline.Setup(x => x.ValidateAsync( @@ -137,7 +137,7 @@ await history.AddAsync(new PasswordHistoryEntry hasher.Object); await using var transaction = - await db.Database.BeginTransactionAsync(); + await db.Database.BeginTransactionAsync(cancellationToken: TestContext.Current.CancellationToken); await Assert.ThrowsAsync(() => writer.ChangeAsync( @@ -149,8 +149,9 @@ await Assert.ThrowsAsync(() => }, Options(2), Now.AddMinutes(1), - default)); + TestContext.Current.CancellationToken)); + // Cleanup must complete even when the test is cancelled. await transaction.RollbackAsync(); } @@ -159,7 +160,7 @@ await Assert.ThrowsAsync(() => { var savedCredential = await verifyDb.PasswordCredentials .AsNoTracking() - .SingleAsync(x => x.Id == credentialId); + .SingleAsync(x => x.Id == credentialId, cancellationToken: TestContext.Current.CancellationToken); Assert.Equal(Hash("current"), savedCredential.SecretHash); Assert.Equal(0, savedCredential.Version); @@ -167,7 +168,7 @@ await Assert.ThrowsAsync(() => var history = await verifyDb.PasswordHistory .AsNoTracking() .Where(x => x.CredentialId == credentialId) - .ToListAsync(); + .ToListAsync(cancellationToken: TestContext.Current.CancellationToken); Assert.Single(history); Assert.Equal(originalHistoryId, history[0].Id); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/PasswordCredentialWriterTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/PasswordCredentialWriterTests.cs index 98f820ae..e2fefb40 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/PasswordCredentialWriterTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/PasswordCredentialWriterTests.cs @@ -102,7 +102,7 @@ public async Task Create_ValidatesAndPersistsHashedPassword() var result = await f.Writer.CreateAsync( Tenant, user, "new-password", - Context(), Options(3), Now, default); + Context(), Options(3), Now, TestContext.Current.CancellationToken); Assert.Same(result, saved); Assert.Equal(user, result.UserKey); @@ -157,7 +157,7 @@ public async Task Change_WithHistory_StoresOldHashAndPrunes() var updated = await f.Writer.ChangeAsync( credential, "new-password", Context(), Options(3), - Now.AddMinutes(1), default); + Now.AddMinutes(1), TestContext.Current.CancellationToken); Assert.Equal(Hash("new-password"), updated.SecretHash); Assert.NotNull(added); @@ -193,7 +193,7 @@ public async Task Change_WithoutHistory_PrunesAllButDoesNotAdd() await f.Writer.ChangeAsync( credential, "new-password", Context(), Options(0), - Now.AddMinutes(1), default); + Now.AddMinutes(1), TestContext.Current.CancellationToken); f.History.Verify(x => x.AddAsync( It.IsAny(), @@ -227,13 +227,13 @@ public async Task ValidationFailure_PreventsPersistence( { await f.Writer.ChangeAsync( Credential(), "reused", - Context(), Options(3), Now, default); + Context(), Options(3), Now, TestContext.Current.CancellationToken); } else { await f.Writer.CreateAsync( Tenant, UserKey.New(), "reused", - Context(), Options(3), Now, default); + Context(), Options(3), Now, TestContext.Current.CancellationToken); } }); @@ -266,7 +266,7 @@ public async Task Change_WhenCredentialSaveFails_DoesNotWriteHistory() await Assert.ThrowsAsync(() => f.Writer.ChangeAsync( Credential(), "new-password", - Context(), Options(3), Now, default)); + Context(), Options(3), Now, TestContext.Current.CancellationToken)); f.HistoryFactory.Verify( x => x.Create(It.IsAny()), @@ -286,7 +286,7 @@ public async Task Change_WhenHistoryAddFails_DoesNotPrune() await Assert.ThrowsAsync(() => f.Writer.ChangeAsync( Credential(), "new-password", - Context(), Options(3), Now, default)); + Context(), Options(3), Now, TestContext.Current.CancellationToken)); f.History.Verify(x => x.PruneAsync( It.IsAny(), @@ -308,7 +308,7 @@ public async Task Change_WhenPruneFails_PropagatesFailure() await Assert.ThrowsAsync(() => f.Writer.ChangeAsync( Credential(), "new-password", - Context(), Options(3), Now, default)); + Context(), Options(3), Now, TestContext.Current.CancellationToken)); f.History.Verify(x => x.AddAsync( It.IsAny(), diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/PasswordValidationPipelineTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/PasswordValidationPipelineTests.cs index e6cab3bf..e2f0bb94 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/PasswordValidationPipelineTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/PasswordValidationPipelineTests.cs @@ -98,7 +98,7 @@ public async Task AbsoluteLimit_CannotBeBypassedByCustomStrengthPolicy() var result = await pipeline.ValidateAsync( CreateRequest(new string('a', 257)), - CreateOptions()); + CreateOptions(), ct: TestContext.Current.CancellationToken); Assert.False(result.Succeeded); @@ -119,7 +119,7 @@ public async Task NewCredential_DoesNotRequireHistory() var result = await pipeline.ValidateAsync( CreateRequest("NewPassword123!"), - CreateOptions(rememberLast: 5)); + CreateOptions(rememberLast: 5), ct: TestContext.Current.CancellationToken); Assert.True(result.Succeeded); Assert.Equal(1, strength.CallCount); @@ -142,7 +142,7 @@ public async Task ExistingCredential_AlwaysValidatesHistory_WhenRememberLastIsZe var result = await pipeline.ValidateAsync( CreateRequest("CurrentPassword123!", key), - CreateOptions(rememberLast: 0)); + CreateOptions(rememberLast: 0), ct: TestContext.Current.CancellationToken); Assert.False(result.Succeeded); @@ -164,7 +164,7 @@ public async Task ExistingCredential_PassesRememberLastToHistoryValidator() var result = await pipeline.ValidateAsync( CreateRequest("NewPassword123!", key), - CreateOptions(rememberLast: 5)); + CreateOptions(rememberLast: 5), ct: TestContext.Current.CancellationToken); Assert.True(result.Succeeded); Assert.Equal(1, history.CallCount); @@ -189,7 +189,7 @@ public async Task ExistingCredential_PropagatesHistoryValidationFailure() var result = await pipeline.ValidateAsync( CreateRequest("PreviouslyUsed123!", key), - CreateOptions(rememberLast: 3)); + CreateOptions(rememberLast: 3), ct: TestContext.Current.CancellationToken); Assert.False(result.Succeeded); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/ResetPasswordTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/ResetPasswordTests.cs index c2b92ed7..abd0ee70 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/ResetPasswordTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/ResetPasswordTests.cs @@ -24,7 +24,7 @@ public async Task Begin_reset_with_token_should_notify_without_exposing_token() var result = await service.BeginResetAsync( BeginContext(), - BeginRequest(ResetCodeType.Token)); + BeginRequest(ResetCodeType.Token), ct: TestContext.Current.CancellationToken); var notification = notifier.Notifications.Should() .ContainSingle().Subject; @@ -67,7 +67,7 @@ public async Task Begin_reset_with_code_should_notify_numeric_code() await runtime.GetCredentialManagementService().BeginResetAsync( BeginContext(), - BeginRequest(ResetCodeType.Code)); + BeginRequest(ResetCodeType.Code), ct: TestContext.Current.CancellationToken); var notification = notifier.Notifications.Should() .ContainSingle().Subject; @@ -86,7 +86,7 @@ public async Task Begin_reset_for_unknown_user_should_not_notify() BeginContext(), BeginRequest( ResetCodeType.Token, - "unknown@test.com")); + "unknown@test.com"), ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result.ExpiresAt.Should().BeAfter(runtime.Clock.UtcNow); @@ -104,7 +104,7 @@ public async Task Reset_password_with_valid_token_should_succeed() var result = await service.CompleteResetAsync( CompleteContext(), - CompleteRequest(token, "newpass123")); + CompleteRequest(token, "newpass123"), ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); } @@ -124,7 +124,7 @@ public async Task Reset_password_with_same_password_should_not_consume_token() // reset must return a generic success response. var samePassword = await service.CompleteResetAsync( CompleteContext(), - CompleteRequest(token, "admin")); + CompleteRequest(token, "admin"), ct: TestContext.Current.CancellationToken); samePassword.IsSuccess.Should().BeTrue(); @@ -134,7 +134,7 @@ public async Task Reset_password_with_same_password_should_not_consume_token() // A policy failure must not consume the reset token. var retry = await service.CompleteResetAsync( CompleteContext(), - CompleteRequest(token, "DifferentPassword123!")); + CompleteRequest(token, "DifferentPassword123!"), ct: TestContext.Current.CancellationToken); retry.IsSuccess.Should().BeTrue(); @@ -144,7 +144,7 @@ await AssertPasswordAsync( // A successfully consumed token must not be reusable. var replay = await service.CompleteResetAsync( CompleteContext(), - CompleteRequest(token, "AnotherPassword123!")); + CompleteRequest(token, "AnotherPassword123!"), ct: TestContext.Current.CancellationToken); replay.IsSuccess.Should().BeTrue(); @@ -174,7 +174,7 @@ public async Task Reset_token_should_lock_after_max_attempts() { var result = await service.CompleteResetAsync( CompleteContext(), - CompleteRequest(wrongToken, "newpass123")); + CompleteRequest(wrongToken, "newpass123"), ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); @@ -184,7 +184,7 @@ public async Task Reset_token_should_lock_after_max_attempts() // Even the correct token must no longer work. var afterLockout = await service.CompleteResetAsync( CompleteContext(), - CompleteRequest(token, "newpass123")); + CompleteRequest(token, "newpass123"), ct: TestContext.Current.CancellationToken); afterLockout.IsSuccess.Should().BeTrue(); @@ -205,7 +205,7 @@ public async Task Reset_token_should_be_single_use() var first = await service.CompleteResetAsync( CompleteContext(), - CompleteRequest(token, "newpass123")); + CompleteRequest(token, "newpass123"), ct: TestContext.Current.CancellationToken); first.IsSuccess.Should().BeTrue(); @@ -213,7 +213,7 @@ public async Task Reset_token_should_be_single_use() var replay = await service.CompleteResetAsync( CompleteContext(), - CompleteRequest(token, "anotherpass")); + CompleteRequest(token, "anotherpass"), ct: TestContext.Current.CancellationToken); replay.IsSuccess.Should().BeTrue(); @@ -241,7 +241,7 @@ public async Task Reset_token_should_not_change_password_if_expired() var result = await service.CompleteResetAsync( CompleteContext(), - CompleteRequest(token, "newpass123")); + CompleteRequest(token, "newpass123"), ct: TestContext.Current.CancellationToken); // Generic anonymous response, not proof of a password change. result.IsSuccess.Should().BeTrue(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/Store/PasswordCredentialStoreContractTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/Store/PasswordCredentialStoreContractTests.cs index c5901281..b74ac3e2 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/Store/PasswordCredentialStoreContractTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/Store/PasswordCredentialStoreContractTests.cs @@ -56,13 +56,13 @@ public async Task AddAsync_PersistsCredential() var user = UserKey.New(); var credential = CreateCredential(TenantA, user, "credential-1"); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); var key = new CredentialKey( credential.Tenant, credential.Id); - var persisted = await store.GetAsync(key); + var persisted = await store.GetAsync(key, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.Id.Should().Be(credential.Id); @@ -82,11 +82,11 @@ public async Task ExistsAsync_WhenCredentialExists_ReturnsTrue() UserKey.New(), "credential-1"); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); var key = new CredentialKey(TenantA, credential.Id); - (await store.ExistsAsync(key)).Should().BeTrue(); + (await store.ExistsAsync(key, ct: TestContext.Current.CancellationToken)).Should().BeTrue(); } [Fact] @@ -102,7 +102,7 @@ public async Task ExistsAsync_WhenCredentialDoesNotExist_ReturnsFalse() var key = new CredentialKey(TenantA, credential.Id); - (await store.ExistsAsync(key)).Should().BeFalse(); + (await store.ExistsAsync(key, ct: TestContext.Current.CancellationToken)).Should().BeFalse(); } [Fact] @@ -117,7 +117,7 @@ public async Task GetAsync_WhenCredentialDoesNotExist_ReturnsNull() "missing"); var result = await store.GetAsync( - new CredentialKey(TenantA, credential.Id)); + new CredentialKey(TenantA, credential.Id), ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -140,7 +140,7 @@ public async Task AddAsync_WhenUserAlreadyHasActivePasswordCredential_ThrowsConf var second = CreateCredential( TenantA, user, "second"); - await store.AddAsync(first); + await store.AddAsync(first, ct: TestContext.Current.CancellationToken); var act = () => store.AddAsync(second); @@ -159,13 +159,13 @@ public async Task AddAsync_AfterSoftDeletingExistingCredential_AllowsReplacement var first = CreateCredential( TenantA, user, "first"); - await store.AddAsync(first); + await store.AddAsync(first, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new CredentialKey(TenantA, first.Id), first.Version, DeleteMode.Soft, - Now); + Now, ct: TestContext.Current.CancellationToken); var replacement = CreateCredential( TenantA, user, "replacement"); @@ -174,7 +174,7 @@ await store.DeleteAsync( await act.Should().NotThrowAsync(); - var active = await store.GetByUserAsync(user); + var active = await store.GetByUserAsync(user, ct: TestContext.Current.CancellationToken); active.Should().ContainSingle(); active.Single().Id.Should().Be(replacement.Id); @@ -195,7 +195,7 @@ public async Task SaveAsync_WhenExpectedVersionMatches_PersistsChanges() UserKey.New(), "original"); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); var changedAt = Now.AddMinutes(10); @@ -205,10 +205,10 @@ public async Task SaveAsync_WhenExpectedVersionMatches_PersistsChanges() await store.SaveAsync( credential, - expectedVersion: 0); + expectedVersion: 0, ct: TestContext.Current.CancellationToken); var persisted = await store.GetAsync( - new CredentialKey(TenantA, credential.Id)); + new CredentialKey(TenantA, credential.Id), ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); @@ -251,14 +251,14 @@ public async Task SaveAsync_WhenExpectedVersionIsStale_ThrowsConcurrency() UserKey.New(), "credential"); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); var changed = credential.Revoke( Now.AddMinutes(5)); await store.SaveAsync( changed, - credential.Version); + credential.Version, ct: TestContext.Current.CancellationToken); var act = () => store.SaveAsync( changed, @@ -283,17 +283,17 @@ public async Task RevokeAsync_RevokesCredential() UserKey.New(), "credential"); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); var revokedAt = Now.AddMinutes(10); await store.RevokeAsync( new CredentialKey(TenantA, credential.Id), revokedAt, - credential.Version); + credential.Version, ct: TestContext.Current.CancellationToken); var persisted = await store.GetAsync( - new CredentialKey(TenantA, credential.Id)); + new CredentialKey(TenantA, credential.Id), ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.Security.RevokedAt.Should().Be(revokedAt); @@ -331,12 +331,12 @@ public async Task RevokeAsync_WhenExpectedVersionIsStale_ThrowsConcurrency() UserKey.New(), "credential"); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); await store.RevokeAsync( new CredentialKey(TenantA, credential.Id), Now, - 0); + 0, ct: TestContext.Current.CancellationToken); var act = () => store.RevokeAsync( new CredentialKey(TenantA, credential.Id), @@ -361,15 +361,15 @@ public async Task DeleteAsync_SoftDelete_HidesCredentialFromGetByUser() var credential = CreateCredential( TenantA, user, "credential"); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new CredentialKey(TenantA, credential.Id), credential.Version, DeleteMode.Soft, - Now); + Now, ct: TestContext.Current.CancellationToken); - var credentials = await store.GetByUserAsync(user); + var credentials = await store.GetByUserAsync(user, ct: TestContext.Current.CancellationToken); credentials.Should().BeEmpty(); } @@ -385,16 +385,16 @@ public async Task DeleteAsync_SoftDelete_PreservesCredentialRecord() UserKey.New(), "credential"); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new CredentialKey(TenantA, credential.Id), 0, DeleteMode.Soft, - Now); + Now, ct: TestContext.Current.CancellationToken); var persisted = await store.GetAsync( - new CredentialKey(TenantA, credential.Id)); + new CredentialKey(TenantA, credential.Id), ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.IsDeleted.Should().BeTrue(); @@ -413,7 +413,7 @@ public async Task DeleteAsync_HardDelete_RemovesCredential() UserKey.New(), "credential"); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); var key = new CredentialKey( TenantA, @@ -423,10 +423,10 @@ await store.DeleteAsync( key, credential.Version, DeleteMode.Hard, - Now); + Now, ct: TestContext.Current.CancellationToken); - (await store.GetAsync(key)).Should().BeNull(); - (await store.ExistsAsync(key)).Should().BeFalse(); + (await store.GetAsync(key, ct: TestContext.Current.CancellationToken)).Should().BeNull(); + (await store.ExistsAsync(key, ct: TestContext.Current.CancellationToken)).Should().BeFalse(); } [Fact] @@ -440,12 +440,12 @@ public async Task DeleteAsync_WhenExpectedVersionIsStale_ThrowsConcurrency() UserKey.New(), "credential"); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); await store.RevokeAsync( new CredentialKey(TenantA, credential.Id), Now, - 0); + 0, ct: TestContext.Current.CancellationToken); var act = () => store.DeleteAsync( new CredentialKey(TenantA, credential.Id), @@ -476,10 +476,10 @@ public async Task GetByUserAsync_ReturnsOnlyActiveNonDeletedCredentialsForUser() var credentialB = CreateCredential( TenantA, userB, "b"); - await store.AddAsync(credentialA); - await store.AddAsync(credentialB); + await store.AddAsync(credentialA, ct: TestContext.Current.CancellationToken); + await store.AddAsync(credentialB, ct: TestContext.Current.CancellationToken); - var result = await store.GetByUserAsync(userA); + var result = await store.GetByUserAsync(userA, ct: TestContext.Current.CancellationToken); result.Should().ContainSingle(); result.Single().Id.Should().Be(credentialA.Id); @@ -500,18 +500,18 @@ public async Task DeleteByUserAsync_SoftDelete_DeletesUsersCredentials() var credential = CreateCredential( TenantA, user, "credential"); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); await store.DeleteByUserAsync( user, DeleteMode.Soft, - Now); + Now, ct: TestContext.Current.CancellationToken); - var active = await store.GetByUserAsync(user); + var active = await store.GetByUserAsync(user, ct: TestContext.Current.CancellationToken); active.Should().BeEmpty(); var persisted = await store.GetAsync( - new CredentialKey(TenantA, credential.Id)); + new CredentialKey(TenantA, credential.Id), ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.IsDeleted.Should().BeTrue(); @@ -530,15 +530,15 @@ public async Task DeleteByUserAsync_HardDelete_RemovesUsersCredentials() var credential = CreateCredential( TenantA, user, "credential"); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); await store.DeleteByUserAsync( user, DeleteMode.Hard, - Now); + Now, ct: TestContext.Current.CancellationToken); var persisted = await store.GetAsync( - new CredentialKey(TenantA, credential.Id)); + new CredentialKey(TenantA, credential.Id), ct: TestContext.Current.CancellationToken); persisted.Should().BeNull(); } @@ -558,20 +558,20 @@ public async Task DeleteByUserAsync_DoesNotAffectOtherUsers() var other = CreateCredential( TenantA, otherUser, "other"); - await store.AddAsync(target); - await store.AddAsync(other); + await store.AddAsync(target, ct: TestContext.Current.CancellationToken); + await store.AddAsync(other, ct: TestContext.Current.CancellationToken); await store.DeleteByUserAsync( targetUser, DeleteMode.Hard, - Now); + Now, ct: TestContext.Current.CancellationToken); (await store.GetAsync( - new CredentialKey(TenantA, target.Id))) + new CredentialKey(TenantA, target.Id), ct: TestContext.Current.CancellationToken)) .Should().BeNull(); (await store.GetAsync( - new CredentialKey(TenantA, other.Id))) + new CredentialKey(TenantA, other.Id), ct: TestContext.Current.CancellationToken)) .Should().NotBeNull(); } @@ -595,11 +595,11 @@ public async Task GetByUserAsync_IsTenantIsolated() var credentialB = CreateCredential( TenantB, user, "b"); - await storeA.AddAsync(credentialA); - await storeB.AddAsync(credentialB); + await storeA.AddAsync(credentialA, ct: TestContext.Current.CancellationToken); + await storeB.AddAsync(credentialB, ct: TestContext.Current.CancellationToken); - var fromA = await storeA.GetByUserAsync(user); - var fromB = await storeB.GetByUserAsync(user); + var fromA = await storeA.GetByUserAsync(user, ct: TestContext.Current.CancellationToken); + var fromB = await storeB.GetByUserAsync(user, ct: TestContext.Current.CancellationToken); fromA.Should().ContainSingle(); fromA.Single().Id.Should().Be(credentialA.Id); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/Store/PasswordHistoryStoreContractTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/Store/PasswordHistoryStoreContractTests.cs index a42a4918..f023807e 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/Store/PasswordHistoryStoreContractTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/Store/PasswordHistoryStoreContractTests.cs @@ -47,9 +47,9 @@ public async Task AddAsync_PersistsAllFields() var key = Key(TenantA); var entry = Entry(key, UserKey.New()); - await store.AddAsync(entry); + await store.AddAsync(entry, ct: TestContext.Current.CancellationToken); - var stored = (await store.GetRecentAsync(key, 10)) + var stored = (await store.GetRecentAsync(key, 10, ct: TestContext.Current.CancellationToken)) .Should().ContainSingle().Subject; stored.Id.Should().Be(entry.Id); @@ -72,11 +72,11 @@ public async Task GetRecentAsync_ReturnsNewestFirst() var newest = Entry(key, user, 3); var middle = Entry(key, user, 2); - await store.AddAsync(newest); - await store.AddAsync(oldest); - await store.AddAsync(middle); + await store.AddAsync(newest, ct: TestContext.Current.CancellationToken); + await store.AddAsync(oldest, ct: TestContext.Current.CancellationToken); + await store.AddAsync(middle, ct: TestContext.Current.CancellationToken); - var result = await store.GetRecentAsync(key, 2); + var result = await store.GetRecentAsync(key, 2, ct: TestContext.Current.CancellationToken); result.Select(x => x.Id) .Should().Equal(newest.Id, middle.Id); @@ -95,10 +95,10 @@ public async Task GetRecentAsync_EqualDates_UsesIdDescending() var high = Entry(key, user, id: Guid.Parse( "00000000-0000-0000-0000-000000000002")); - await store.AddAsync(low); - await store.AddAsync(high); + await store.AddAsync(low, ct: TestContext.Current.CancellationToken); + await store.AddAsync(high, ct: TestContext.Current.CancellationToken); - var result = await store.GetRecentAsync(key, 2); + var result = await store.GetRecentAsync(key, 2, ct: TestContext.Current.CancellationToken); result.Select(x => x.Id) .Should().Equal(high.Id, low.Id); @@ -111,9 +111,9 @@ public async Task GetRecentAsync_Zero_ReturnsEmpty() var store = db.CreateStore(TenantA); var key = Key(TenantA); - await store.AddAsync(Entry(key, UserKey.New())); + await store.AddAsync(Entry(key, UserKey.New()), ct: TestContext.Current.CancellationToken); - (await store.GetRecentAsync(key, 0)) + (await store.GetRecentAsync(key, 0, ct: TestContext.Current.CancellationToken)) .Should().BeEmpty(); } @@ -124,9 +124,9 @@ public async Task GetRecentAsync_CountGreaterThanAvailable_ReturnsAll() var store = db.CreateStore(TenantA); var key = Key(TenantA); - await store.AddAsync(Entry(key, UserKey.New())); + await store.AddAsync(Entry(key, UserKey.New()), ct: TestContext.Current.CancellationToken); - (await store.GetRecentAsync(key, 100)) + (await store.GetRecentAsync(key, 100, ct: TestContext.Current.CancellationToken)) .Should().ContainSingle(); } @@ -148,7 +148,7 @@ public async Task GetRecentAsync_MissingCredential_ReturnsEmpty() await using var db = await CreateDatabaseAsync(); var store = db.CreateStore(TenantA); - (await store.GetRecentAsync(Key(TenantA), 5)) + (await store.GetRecentAsync(Key(TenantA), 5, ct: TestContext.Current.CancellationToken)) .Should().BeEmpty(); } @@ -162,10 +162,10 @@ public async Task GetRecentAsync_DoesNotMixCredentials() var user = UserKey.New(); var a = Entry(keyA, user); - await store.AddAsync(a); - await store.AddAsync(Entry(keyB, user)); + await store.AddAsync(a, ct: TestContext.Current.CancellationToken); + await store.AddAsync(Entry(keyB, user), ct: TestContext.Current.CancellationToken); - var result = await store.GetRecentAsync(keyA, 10); + var result = await store.GetRecentAsync(keyA, 10, ct: TestContext.Current.CancellationToken); result.Should().ContainSingle(); result.Single().Id.Should().Be(a.Id); @@ -189,11 +189,11 @@ public async Task PruneAsync_KeepsExactlyNewestEntries( .ToArray(); foreach (var entry in entries.Reverse()) - await store.AddAsync(entry); + await store.AddAsync(entry, ct: TestContext.Current.CancellationToken); - await store.PruneAsync(key, keepLast); + await store.PruneAsync(key, keepLast, ct: TestContext.Current.CancellationToken); - var result = await store.GetRecentAsync(key, 20); + var result = await store.GetRecentAsync(key, 20, ct: TestContext.Current.CancellationToken); var expected = entries .OrderByDescending(x => x.ReplacedAt) .Take(keepLast) @@ -211,14 +211,14 @@ public async Task PruneAsync_DoesNotAffectOtherCredentials() var keyB = Key(TenantA); var user = UserKey.New(); - await store.AddAsync(Entry(keyA, user)); + await store.AddAsync(Entry(keyA, user), ct: TestContext.Current.CancellationToken); var other = Entry(keyB, user); - await store.AddAsync(other); + await store.AddAsync(other, ct: TestContext.Current.CancellationToken); - await store.PruneAsync(keyA, 0); + await store.PruneAsync(keyA, 0, ct: TestContext.Current.CancellationToken); - (await store.GetRecentAsync(keyA, 10)).Should().BeEmpty(); - (await store.GetRecentAsync(keyB, 10)) + (await store.GetRecentAsync(keyA, 10, ct: TestContext.Current.CancellationToken)).Should().BeEmpty(); + (await store.GetRecentAsync(keyB, 10, ct: TestContext.Current.CancellationToken)) .Should().ContainSingle(x => x.Id == other.Id); } @@ -242,14 +242,14 @@ public async Task PruneAsync_IsIdempotent() var key = Key(TenantA); var user = UserKey.New(); - await store.AddAsync(Entry(key, user, 1)); - await store.AddAsync(Entry(key, user, 2)); + await store.AddAsync(Entry(key, user, 1), ct: TestContext.Current.CancellationToken); + await store.AddAsync(Entry(key, user, 2), ct: TestContext.Current.CancellationToken); - await store.PruneAsync(key, 1); - var first = await store.GetRecentAsync(key, 10); + await store.PruneAsync(key, 1, ct: TestContext.Current.CancellationToken); + var first = await store.GetRecentAsync(key, 10, ct: TestContext.Current.CancellationToken); - await store.PruneAsync(key, 1); - var second = await store.GetRecentAsync(key, 10); + await store.PruneAsync(key, 1, ct: TestContext.Current.CancellationToken); + var second = await store.GetRecentAsync(key, 10, ct: TestContext.Current.CancellationToken); second.Should().BeEquivalentTo(first); } @@ -263,15 +263,15 @@ public async Task DeleteByCredentialAsync_RemovesOnlyTarget() var keyB = Key(TenantA); var user = UserKey.New(); - await store.AddAsync(Entry(keyA, user)); - await store.AddAsync(Entry(keyA, user)); + await store.AddAsync(Entry(keyA, user), ct: TestContext.Current.CancellationToken); + await store.AddAsync(Entry(keyA, user), ct: TestContext.Current.CancellationToken); var other = Entry(keyB, user); - await store.AddAsync(other); + await store.AddAsync(other, ct: TestContext.Current.CancellationToken); - await store.DeleteByCredentialAsync(keyA); + await store.DeleteByCredentialAsync(keyA, ct: TestContext.Current.CancellationToken); - (await store.GetRecentAsync(keyA, 10)).Should().BeEmpty(); - (await store.GetRecentAsync(keyB, 10)) + (await store.GetRecentAsync(keyA, 10, ct: TestContext.Current.CancellationToken)).Should().BeEmpty(); + (await store.GetRecentAsync(keyB, 10, ct: TestContext.Current.CancellationToken)) .Should().ContainSingle(x => x.Id == other.Id); } @@ -283,10 +283,10 @@ public async Task DeleteByCredentialAsync_Missing_IsIdempotent() var key = Key(TenantA); - await store.DeleteByCredentialAsync(key); - await store.DeleteByCredentialAsync(key); + await store.DeleteByCredentialAsync(key, ct: TestContext.Current.CancellationToken); + await store.DeleteByCredentialAsync(key, ct: TestContext.Current.CancellationToken); - (await store.GetRecentAsync(key, 10)).Should().BeEmpty(); + (await store.GetRecentAsync(key, 10, ct: TestContext.Current.CancellationToken)).Should().BeEmpty(); } [Fact] @@ -300,16 +300,16 @@ public async Task DeleteByUserAsync_RemovesAllUsersCredentials() var keyA2 = Key(TenantA); var keyB = Key(TenantA); - await store.AddAsync(Entry(keyA1, userA)); - await store.AddAsync(Entry(keyA2, userA)); + await store.AddAsync(Entry(keyA1, userA), ct: TestContext.Current.CancellationToken); + await store.AddAsync(Entry(keyA2, userA), ct: TestContext.Current.CancellationToken); var other = Entry(keyB, userB); - await store.AddAsync(other); + await store.AddAsync(other, ct: TestContext.Current.CancellationToken); - await store.DeleteByUserAsync(userA); + await store.DeleteByUserAsync(userA, ct: TestContext.Current.CancellationToken); - (await store.GetRecentAsync(keyA1, 10)).Should().BeEmpty(); - (await store.GetRecentAsync(keyA2, 10)).Should().BeEmpty(); - (await store.GetRecentAsync(keyB, 10)) + (await store.GetRecentAsync(keyA1, 10, ct: TestContext.Current.CancellationToken)).Should().BeEmpty(); + (await store.GetRecentAsync(keyA2, 10, ct: TestContext.Current.CancellationToken)).Should().BeEmpty(); + (await store.GetRecentAsync(keyB, 10, ct: TestContext.Current.CancellationToken)) .Should().ContainSingle(x => x.Id == other.Id); } @@ -321,8 +321,8 @@ public async Task DeleteByUserAsync_Missing_IsIdempotent() var user = UserKey.New(); - await store.DeleteByUserAsync(user); - await store.DeleteByUserAsync(user); + await store.DeleteByUserAsync(user, ct: TestContext.Current.CancellationToken); + await store.DeleteByUserAsync(user, ct: TestContext.Current.CancellationToken); } [Fact] @@ -337,18 +337,18 @@ public async Task TenantIsolation_GetRecentAndPrune() var keyA = new CredentialKey(TenantA, credentialId); var keyB = new CredentialKey(TenantB, credentialId); - await storeA.AddAsync(Entry(keyA, user)); - await storeB.AddAsync(Entry(keyB, user)); + await storeA.AddAsync(Entry(keyA, user), ct: TestContext.Current.CancellationToken); + await storeB.AddAsync(Entry(keyB, user), ct: TestContext.Current.CancellationToken); - (await storeA.GetRecentAsync(keyA, 10)) + (await storeA.GetRecentAsync(keyA, 10, ct: TestContext.Current.CancellationToken)) .Should().ContainSingle(); - (await storeB.GetRecentAsync(keyB, 10)) + (await storeB.GetRecentAsync(keyB, 10, ct: TestContext.Current.CancellationToken)) .Should().ContainSingle(); - await storeA.PruneAsync(keyA, 0); + await storeA.PruneAsync(keyA, 0, ct: TestContext.Current.CancellationToken); - (await storeA.GetRecentAsync(keyA, 10)).Should().BeEmpty(); - (await storeB.GetRecentAsync(keyB, 10)) + (await storeA.GetRecentAsync(keyA, 10, ct: TestContext.Current.CancellationToken)).Should().BeEmpty(); + (await storeB.GetRecentAsync(keyB, 10, ct: TestContext.Current.CancellationToken)) .Should().ContainSingle(); } @@ -364,13 +364,13 @@ public async Task TenantIsolation_DeleteByUser() var keyA = new CredentialKey(TenantA, credentialId); var keyB = new CredentialKey(TenantB, credentialId); - await storeA.AddAsync(Entry(keyA, user)); - await storeB.AddAsync(Entry(keyB, user)); + await storeA.AddAsync(Entry(keyA, user), ct: TestContext.Current.CancellationToken); + await storeB.AddAsync(Entry(keyB, user), ct: TestContext.Current.CancellationToken); - await storeA.DeleteByUserAsync(user); + await storeA.DeleteByUserAsync(user, ct: TestContext.Current.CancellationToken); - (await storeA.GetRecentAsync(keyA, 10)).Should().BeEmpty(); - (await storeB.GetRecentAsync(keyB, 10)) + (await storeA.GetRecentAsync(keyA, 10, ct: TestContext.Current.CancellationToken)).Should().BeEmpty(); + (await storeB.GetRecentAsync(keyB, 10, ct: TestContext.Current.CancellationToken)) .Should().ContainSingle(); } @@ -456,7 +456,7 @@ public async Task AddAsync_DuplicateId_Throws() var key = Key(TenantA); var entry = Entry(key, UserKey.New()); - await store.AddAsync(entry); + await store.AddAsync(entry, ct: TestContext.Current.CancellationToken); var act = () => store.AddAsync(entry); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/UAuthPasswordHistoryValidatorTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/UAuthPasswordHistoryValidatorTests.cs index a1c1631e..128150bf 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/UAuthPasswordHistoryValidatorTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Credentials/UAuthPasswordHistoryValidatorTests.cs @@ -109,7 +109,7 @@ public async Task CurrentPassword_IsAlwaysRejected( var f = Create(); var result = await f.Validator.ValidateAsync( - f.Key, "CurrentPassword123!", rememberLast); + f.Key, "CurrentPassword123!", rememberLast, ct: TestContext.Current.CancellationToken); result.Succeeded.Should().BeFalse(); result.Violations.Should().ContainSingle( @@ -126,7 +126,7 @@ public async Task RememberLastZero_AllowsDifferentPassword_WithoutHistoryLookup( var f = Create(); var result = await f.Validator.ValidateAsync( - f.Key, "DifferentPassword123!", 0); + f.Key, "DifferentPassword123!", 0, ct: TestContext.Current.CancellationToken); result.Succeeded.Should().BeTrue(); @@ -151,7 +151,7 @@ public async Task HistoricalPassword_IsRejected() }); var result = await f.Validator.ValidateAsync( - f.Key, "Previous2!", 3); + f.Key, "Previous2!", 3, ct: TestContext.Current.CancellationToken); result.Succeeded.Should().BeFalse(); result.Violations.Should().ContainSingle( @@ -173,7 +173,7 @@ public async Task NewPassword_NotPresentInHistory_IsAllowed() }); var result = await f.Validator.ValidateAsync( - f.Key, "EntirelyNewPassword123!", 2); + f.Key, "EntirelyNewPassword123!", 2, ct: TestContext.Current.CancellationToken); result.Succeeded.Should().BeTrue(); } @@ -189,7 +189,7 @@ public async Task EmptyHistory_AllowsDifferentPassword() .ReturnsAsync(Array.Empty()); var result = await f.Validator.ValidateAsync( - f.Key, "NewPassword123!", 5); + f.Key, "NewPassword123!", 5, ct: TestContext.Current.CancellationToken); result.Succeeded.Should().BeTrue(); } @@ -205,7 +205,7 @@ public async Task HistoryLookup_UsesRequestedLimit() .ReturnsAsync(Array.Empty()); await f.Validator.ValidateAsync( - f.Key, "NewPassword123!", 7); + f.Key, "NewPassword123!", 7, ct: TestContext.Current.CancellationToken); f.HistoryStore.Verify( x => x.GetRecentAsync( @@ -268,7 +268,7 @@ public async Task HistoryVerification_StopsAtFirstMatch() }); var result = await f.Validator.ValidateAsync( - f.Key, "Match123!", 3); + f.Key, "Match123!", 3, ct: TestContext.Current.CancellationToken); result.Succeeded.Should().BeFalse(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreAuthenticationStoreTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreAuthenticationStoreTests.cs index 6239983e..313366d1 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreAuthenticationStoreTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreAuthenticationStoreTests.cs @@ -30,9 +30,9 @@ public async Task Add_And_Get_Should_Work() tenant, userKey); - await store.AddAsync(state); + await store.AddAsync(state, ct: TestContext.Current.CancellationToken); - var result = await store.GetAsync(userKey, AuthenticationSecurityScope.Account, null); + var result = await store.GetAsync(userKey, AuthenticationSecurityScope.Account, null, ct: TestContext.Current.CancellationToken); Assert.NotNull(result); Assert.Equal(state.Id, result!.Id); @@ -51,13 +51,13 @@ public async Task Update_With_RegisterFailure_Should_Increment_Version() await using (var db1 = CreateDb(connection)) { var store = new EfCoreAuthenticationSecurityStateStore(db1, new TenantExecutionContext(tenant)); - await store.AddAsync(state); + await store.AddAsync(state, ct: TestContext.Current.CancellationToken); } await using (var db2 = CreateDb(connection)) { var store = new EfCoreAuthenticationSecurityStateStore(db2, new TenantExecutionContext(tenant)); - var existing = await store.GetAsync(userKey, AuthenticationSecurityScope.Account, null); + var existing = await store.GetAsync(userKey, AuthenticationSecurityScope.Account, null, ct: TestContext.Current.CancellationToken); var updated = existing!.RegisterFailure( DateTimeOffset.UtcNow, @@ -65,13 +65,13 @@ public async Task Update_With_RegisterFailure_Should_Increment_Version() failureWindow: TimeSpan.FromMinutes(5), lockoutDuration: TimeSpan.FromMinutes(5)); - await store.UpdateAsync(updated, expectedVersion: 0); + await store.UpdateAsync(updated, expectedVersion: 0, ct: TestContext.Current.CancellationToken); } await using (var db3 = CreateDb(connection)) { var store = new EfCoreAuthenticationSecurityStateStore(db3, new TenantExecutionContext(tenant)); - var result = await store.GetAsync(userKey, AuthenticationSecurityScope.Account, null); + var result = await store.GetAsync(userKey, AuthenticationSecurityScope.Account, null, ct: TestContext.Current.CancellationToken); Assert.Equal(1, result!.SecurityVersion); Assert.Equal(1, result.FailedAttempts); @@ -89,10 +89,10 @@ public async Task Update_With_Wrong_Version_Should_Throw() var userKey = UserKey.FromGuid(Guid.NewGuid()); var state = AuthenticationSecurityState.CreateAccount(tenant, userKey); - await store.AddAsync(state); + await store.AddAsync(state, ct: TestContext.Current.CancellationToken); var updated = state.RegisterFailure(DateTimeOffset.UtcNow, 3, TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(5)); - await Assert.ThrowsAsync(() => store.UpdateAsync(updated, expectedVersion: 999)); + await Assert.ThrowsAsync(() => store.UpdateAsync(updated, expectedVersion: 999, ct: TestContext.Current.CancellationToken)); } [Fact] @@ -109,21 +109,21 @@ public async Task RegisterSuccess_Should_Clear_Failures() await using (var db1 = CreateDb(connection)) { var store = new EfCoreAuthenticationSecurityStateStore(db1, new TenantExecutionContext(tenant)); - await store.AddAsync(state); + await store.AddAsync(state, ct: TestContext.Current.CancellationToken); } await using (var db2 = CreateDb(connection)) { var store = new EfCoreAuthenticationSecurityStateStore(db2, new TenantExecutionContext(tenant)); - var existing = await store.GetAsync(userKey, AuthenticationSecurityScope.Account, null); + var existing = await store.GetAsync(userKey, AuthenticationSecurityScope.Account, null, ct: TestContext.Current.CancellationToken); var updated = existing!.RegisterSuccess(); - await store.UpdateAsync(updated, expectedVersion: 1); + await store.UpdateAsync(updated, expectedVersion: 1, ct: TestContext.Current.CancellationToken); } await using (var db3 = CreateDb(connection)) { var store = new EfCoreAuthenticationSecurityStateStore(db3, new TenantExecutionContext(tenant)); - var result = await store.GetAsync(userKey, AuthenticationSecurityScope.Account, null); + var result = await store.GetAsync(userKey, AuthenticationSecurityScope.Account, null, ct: TestContext.Current.CancellationToken); Assert.Equal(0, result!.FailedAttempts); Assert.Null(result.LockedUntil); @@ -142,7 +142,7 @@ public async Task BeginReset_And_Consume_Should_Work() await using (var db1 = CreateDb(connection)) { var store = new EfCoreAuthenticationSecurityStateStore(db1, new TenantExecutionContext(tenant)); - await store.AddAsync(state); + await store.AddAsync(state, ct: TestContext.Current.CancellationToken); } DateTimeOffset now = DateTimeOffset.UtcNow; @@ -150,23 +150,23 @@ public async Task BeginReset_And_Consume_Should_Work() await using (var db2 = CreateDb(connection)) { var store = new EfCoreAuthenticationSecurityStateStore(db2, new TenantExecutionContext(tenant)); - var existing = await store.GetAsync(userKey, AuthenticationSecurityScope.Account, null); + var existing = await store.GetAsync(userKey, AuthenticationSecurityScope.Account, null, ct: TestContext.Current.CancellationToken); var updated = existing!.BeginReset("hash", now, TimeSpan.FromMinutes(10)); - await store.UpdateAsync(updated, expectedVersion: 0); + await store.UpdateAsync(updated, expectedVersion: 0, ct: TestContext.Current.CancellationToken); } await using (var db3 = CreateDb(connection)) { var store = new EfCoreAuthenticationSecurityStateStore(db3, new TenantExecutionContext(tenant)); - var existing = await store.GetAsync(userKey, AuthenticationSecurityScope.Account, null); + var existing = await store.GetAsync(userKey, AuthenticationSecurityScope.Account, null, ct: TestContext.Current.CancellationToken); var consumed = existing!.ConsumeReset(DateTimeOffset.UtcNow); - await store.UpdateAsync(consumed, expectedVersion: 1); + await store.UpdateAsync(consumed, expectedVersion: 1, ct: TestContext.Current.CancellationToken); } await using (var db4 = CreateDb(connection)) { var store = new EfCoreAuthenticationSecurityStateStore(db4, new TenantExecutionContext(tenant)); - var result = await store.GetAsync(userKey, AuthenticationSecurityScope.Account, null); + var result = await store.GetAsync(userKey, AuthenticationSecurityScope.Account, null, ct: TestContext.Current.CancellationToken); Assert.NotNull(result!.ResetConsumedAt); } } @@ -184,11 +184,11 @@ public async Task Delete_Should_Work() var state = AuthenticationSecurityState.CreateAccount(tenant, userKey); - await store.AddAsync(state); + await store.AddAsync(state, ct: TestContext.Current.CancellationToken); - await store.DeleteAsync(userKey, AuthenticationSecurityScope.Account, null); + await store.DeleteAsync(userKey, AuthenticationSecurityScope.Account, null, ct: TestContext.Current.CancellationToken); - var result = await store.GetAsync(userKey, AuthenticationSecurityScope.Account, null); + var result = await store.GetAsync(userKey, AuthenticationSecurityScope.Account, null, ct: TestContext.Current.CancellationToken); Assert.Null(result); } @@ -207,8 +207,8 @@ public async Task Should_Not_See_Data_From_Other_Tenant() var userKey = UserKey.FromGuid(Guid.NewGuid()); var state = AuthenticationSecurityState.CreateAccount(tenant1, userKey); - await store1.AddAsync(state); - var result = await store2.GetAsync(userKey, AuthenticationSecurityScope.Account, null); + await store1.AddAsync(state, ct: TestContext.Current.CancellationToken); + var result = await store2.GetAsync(userKey, AuthenticationSecurityScope.Account, null, ct: TestContext.Current.CancellationToken); Assert.Null(result); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreCredentialAtomicityTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreCredentialAtomicityTests.cs index b06267a9..b695ae8d 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreCredentialAtomicityTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreCredentialAtomicityTests.cs @@ -49,7 +49,7 @@ public async Task ChangeSecret_WhenSessionRevocationFails_ShouldRollbackCredenti db, new TenantExecutionContext(tenant)); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); } // Act: Change password, then simulate a session failure. @@ -60,7 +60,7 @@ public async Task ChangeSecret_WhenSessionRevocationFails_ShouldRollbackCredenti new TenantExecutionContext(tenant)); var existing = await store.GetAsync( - new CredentialKey(tenant, credential.Id)); + new CredentialKey(tenant, credential.Id), ct: TestContext.Current.CancellationToken); Assert.NotNull(existing); @@ -68,7 +68,7 @@ await Assert.ThrowsAsync( async () => { await using var transaction = - await db.Database.BeginTransactionAsync(); + await db.Database.BeginTransactionAsync(cancellationToken: TestContext.Current.CancellationToken); var updated = existing.ChangeSecret( hasher.Hash("new-password"), @@ -76,7 +76,7 @@ await Assert.ThrowsAsync( await store.SaveAsync( updated, - existing.Version); + existing.Version, ct: TestContext.Current.CancellationToken); // Simulate a failure during session revocation. throw new InvalidOperationException( @@ -92,7 +92,7 @@ await store.SaveAsync( new TenantExecutionContext(tenant)); var persisted = await store.GetAsync( - new CredentialKey(tenant, credential.Id)); + new CredentialKey(tenant, credential.Id), ct: TestContext.Current.CancellationToken); Assert.NotNull(persisted); @@ -135,7 +135,7 @@ public async Task AtomicExecutor_WhenOperationSucceeds_ShouldCommitCredential() db, new TenantExecutionContext(tenant)); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) @@ -173,7 +173,7 @@ await store.SaveAsync( ct); Assert.NotNull(db.Database.CurrentTransaction); - }); + }, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) @@ -183,7 +183,7 @@ await store.SaveAsync( new TenantExecutionContext(tenant)); var persisted = await store.GetAsync( - new CredentialKey(tenant, credential.Id)); + new CredentialKey(tenant, credential.Id), ct: TestContext.Current.CancellationToken); Assert.NotNull(persisted); Assert.True( @@ -217,7 +217,7 @@ public async Task AtomicExecutor_WhenOperationFails_ShouldRollbackCredential() db, new TenantExecutionContext(tenant)); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) @@ -258,7 +258,7 @@ await store.SaveAsync( throw new InvalidOperationException( "Simulated session revocation failure"); - })); + }, ct: TestContext.Current.CancellationToken)); } await using (var db = CreateDb(connection)) @@ -268,7 +268,7 @@ await store.SaveAsync( new TenantExecutionContext(tenant)); var persisted = await store.GetAsync( - new CredentialKey(tenant, credential.Id)); + new CredentialKey(tenant, credential.Id), ct: TestContext.Current.CancellationToken); Assert.NotNull(persisted); @@ -343,14 +343,14 @@ public async Task AtomicExecutor_WhenSessionOperationFails_ShouldRollbackBothSto db, new TenantExecutionContext(tenant)); - await credentialStore.AddAsync(credential); + await credentialStore.AddAsync(credential, ct: TestContext.Current.CancellationToken); await sessionStore.ExecuteAsync(async ct => { await sessionStore.CreateRootAsync(root, ct); await sessionStore.CreateChainAsync(chain, ct); await sessionStore.CreateSessionAsync(session, ct); - }); + }, ct: TestContext.Current.CancellationToken); } // Act: Change password and revoke session within one atomic operation. @@ -409,7 +409,7 @@ await sessionStore.RevokeAllChainsAsync( // Session operation succeeded, but the outer operation fails. throw new InvalidOperationException( "Simulated failure before outer commit"); - })); + }, ct: TestContext.Current.CancellationToken)); } // Assert: Both credential and session changes must be rolled back. @@ -426,7 +426,7 @@ await sessionStore.RevokeAllChainsAsync( new TenantExecutionContext(tenant)); var persisted = await credentialStore.GetAsync( - new CredentialKey(tenant, credential.Id)); + new CredentialKey(tenant, credential.Id), ct: TestContext.Current.CancellationToken); Assert.NotNull(persisted); @@ -437,7 +437,7 @@ await sessionStore.RevokeAllChainsAsync( Assert.Equal(0, persisted.Version); - var persistedSession = await sessionStore.GetSessionAsync(sessionId); + var persistedSession = await sessionStore.GetSessionAsync(sessionId, ct: TestContext.Current.CancellationToken); Assert.NotNull(persistedSession); Assert.False(persistedSession.IsRevoked); @@ -481,9 +481,9 @@ public async Task AtomicReset_WhenSessionRevocationFails_ShouldRollbackSecurityS db, new TenantExecutionContext(tenant)); - await credentialStore.AddAsync(credential); + await credentialStore.AddAsync(credential, ct: TestContext.Current.CancellationToken); - await securityStore.AddAsync(securityState); + await securityStore.AddAsync(securityState, ct: TestContext.Current.CancellationToken); var activeReset = securityState.BeginReset( resetTokenHash, @@ -492,7 +492,7 @@ public async Task AtomicReset_WhenSessionRevocationFails_ShouldRollbackSecurityS await securityStore.UpdateAsync( activeReset, - securityState.SecurityVersion); + securityState.SecurityVersion, ct: TestContext.Current.CancellationToken); } // Act: Consume reset token and change password, @@ -572,7 +572,7 @@ await sessionStore.RevokeAllChainsAsync( throw new InvalidOperationException( "Simulated failure before reset commit"); - })); + }, ct: TestContext.Current.CancellationToken)); } // Assert: Both security state and credential must be unchanged. @@ -589,7 +589,7 @@ await sessionStore.RevokeAllChainsAsync( new TenantExecutionContext(tenant)); var persistedCredential = await credentialStore.GetAsync( - new CredentialKey(tenant, credential.Id)); + new CredentialKey(tenant, credential.Id), ct: TestContext.Current.CancellationToken); Assert.NotNull(persistedCredential); @@ -608,7 +608,7 @@ await sessionStore.RevokeAllChainsAsync( var persistedState = await securityStore.GetAsync( user, AuthenticationSecurityScope.Account, - null); + null, ct: TestContext.Current.CancellationToken); Assert.NotNull(persistedState); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreCredentialStoreTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreCredentialStoreTests.cs index 094b9036..d4da21a4 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreCredentialStoreTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreCredentialStoreTests.cs @@ -42,9 +42,9 @@ public async Task Add_And_Get_Should_Work() new CredentialMetadata(), DateTimeOffset.UtcNow); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); - var result = await store.GetAsync(new CredentialKey(tenant, credential.Id)); + var result = await store.GetAsync(new CredentialKey(tenant, credential.Id), ct: TestContext.Current.CancellationToken); Assert.NotNull(result); @@ -75,9 +75,9 @@ public async Task Exists_Should_Return_True_When_Exists() new CredentialMetadata(), DateTimeOffset.UtcNow); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); - var exists = await store.ExistsAsync(new CredentialKey(tenant, credential.Id)); + var exists = await store.ExistsAsync(new CredentialKey(tenant, credential.Id), ct: TestContext.Current.CancellationToken); Assert.True(exists); } @@ -103,25 +103,25 @@ public async Task Save_Should_Increment_Version_And_Update_Hash() await using (var db1 = CreateDb(connection)) { var store = new EfCorePasswordCredentialStore(db1, new TenantExecutionContext(tenant)); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); } await using (var db2 = CreateDb(connection)) { var store = new EfCorePasswordCredentialStore(db2, new TenantExecutionContext(tenant)); - var existing = await store.GetAsync(new CredentialKey(tenant, credential.Id)); + var existing = await store.GetAsync(new CredentialKey(tenant, credential.Id), ct: TestContext.Current.CancellationToken); var updated = existing!.ChangeSecret(hasher.Hash("new"), DateTimeOffset.UtcNow); - await store.SaveAsync(updated, expectedVersion: 0); + await store.SaveAsync(updated, expectedVersion: 0, ct: TestContext.Current.CancellationToken); } await using (var db3 = CreateDb(connection)) { var store = new EfCorePasswordCredentialStore(db3, new TenantExecutionContext(tenant)); - var result = await store.GetAsync(new CredentialKey(tenant, credential.Id)); + var result = await store.GetAsync(new CredentialKey(tenant, credential.Id), ct: TestContext.Current.CancellationToken); Assert.Equal(1, result!.Version); Assert.True(hasher.Verify(result.SecretHash, "new")); @@ -149,18 +149,18 @@ public async Task Save_With_Wrong_Version_Should_Throw() await using (var db1 = CreateDb(connection)) { var store = new EfCorePasswordCredentialStore(db1, new TenantExecutionContext(tenant)); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); } await using (var db2 = CreateDb(connection)) { var store = new EfCorePasswordCredentialStore(db2, new TenantExecutionContext(tenant)); - var existing = await store.GetAsync(new CredentialKey(tenant, credential.Id)); + var existing = await store.GetAsync(new CredentialKey(tenant, credential.Id), ct: TestContext.Current.CancellationToken); var updated = existing!.ChangeSecret(hasher.Hash("new"), DateTimeOffset.UtcNow); await Assert.ThrowsAsync(() => - store.SaveAsync(updated, expectedVersion: 999)); + store.SaveAsync(updated, expectedVersion: 999, ct: TestContext.Current.CancellationToken)); } } @@ -189,9 +189,9 @@ public async Task Should_Not_See_Data_From_Other_Tenant() new CredentialMetadata(), DateTimeOffset.UtcNow); - await store1.AddAsync(credential); + await store1.AddAsync(credential, ct: TestContext.Current.CancellationToken); - var result = await store2.GetAsync(new CredentialKey(tenant2, credential.Id)); + var result = await store2.GetAsync(new CredentialKey(tenant2, credential.Id), ct: TestContext.Current.CancellationToken); Assert.Null(result); } @@ -218,15 +218,15 @@ public async Task Soft_Delete_Should_Work() new CredentialMetadata(), DateTimeOffset.UtcNow); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new CredentialKey(tenant, credential.Id), expectedVersion: 0, DeleteMode.Soft, - DateTimeOffset.UtcNow); + DateTimeOffset.UtcNow, ct: TestContext.Current.CancellationToken); - var result = await store.GetAsync(new CredentialKey(tenant, credential.Id)); + var result = await store.GetAsync(new CredentialKey(tenant, credential.Id), ct: TestContext.Current.CancellationToken); Assert.NotNull(result); Assert.NotNull(result!.DeletedAt); @@ -253,24 +253,24 @@ public async Task Revoke_Should_Persist() await using (var db1 = CreateDb(connection)) { var store = new EfCorePasswordCredentialStore(db1, new TenantExecutionContext(tenant)); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); } await using (var db2 = CreateDb(connection)) { var store = new EfCorePasswordCredentialStore(db2, new TenantExecutionContext(tenant)); - var existing = await store.GetAsync(new CredentialKey(tenant, credential.Id)); + var existing = await store.GetAsync(new CredentialKey(tenant, credential.Id), ct: TestContext.Current.CancellationToken); var revoked = existing!.Revoke(DateTimeOffset.UtcNow); - await store.SaveAsync(revoked, expectedVersion: 0); + await store.SaveAsync(revoked, expectedVersion: 0, ct: TestContext.Current.CancellationToken); } await using (var db3 = CreateDb(connection)) { var store = new EfCorePasswordCredentialStore(db3, new TenantExecutionContext(tenant)); - var result = await store.GetAsync(new CredentialKey(tenant, credential.Id)); + var result = await store.GetAsync(new CredentialKey(tenant, credential.Id), ct: TestContext.Current.CancellationToken); Assert.True(result!.IsRevoked); } @@ -297,24 +297,24 @@ public async Task ChangeSecret_Should_Update_SecurityState() await using (var db1 = CreateDb(connection)) { var store = new EfCorePasswordCredentialStore(db1, new TenantExecutionContext(tenant)); - await store.AddAsync(credential); + await store.AddAsync(credential, ct: TestContext.Current.CancellationToken); } await using (var db2 = CreateDb(connection)) { var store = new EfCorePasswordCredentialStore(db2, new TenantExecutionContext(tenant)); - var existing = await store.GetAsync(new CredentialKey(tenant, credential.Id)); + var existing = await store.GetAsync(new CredentialKey(tenant, credential.Id), ct: TestContext.Current.CancellationToken); var updated = existing!.ChangeSecret(hasher.Hash("new"), DateTimeOffset.UtcNow); - await store.SaveAsync(updated, expectedVersion: 0); + await store.SaveAsync(updated, expectedVersion: 0, ct: TestContext.Current.CancellationToken); } await using (var db3 = CreateDb(connection)) { var store = new EfCorePasswordCredentialStore(db3, new TenantExecutionContext(tenant)); - var result = await store.GetAsync(new CredentialKey(tenant, credential.Id)); + var result = await store.GetAsync(new CredentialKey(tenant, credential.Id), ct: TestContext.Current.CancellationToken); Assert.True(hasher.Verify(result!.SecretHash, "new")); Assert.NotNull(result.UpdatedAt); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreRoleStoreTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreRoleStoreTests.cs index 79d725a7..263a1d93 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreRoleStoreTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreRoleStoreTests.cs @@ -35,9 +35,9 @@ public async Task Add_And_Get_Should_Work() new[] { Permission.From("read"), Permission.From("write") }, DateTimeOffset.UtcNow); - await store.AddAsync(role); + await store.AddAsync(role, ct: TestContext.Current.CancellationToken); - var result = await store.GetAsync(new RoleKey(tenant, role.Id)); + var result = await store.GetAsync(new RoleKey(tenant, role.Id), ct: TestContext.Current.CancellationToken); Assert.NotNull(result); Assert.Equal("admin", result!.Name); @@ -56,9 +56,9 @@ public async Task Add_With_Duplicate_Name_Should_Throw() var role1 = Role.Create(null, tenant, "admin", null, DateTimeOffset.UtcNow); var role2 = Role.Create(null, tenant, "ADMIN", null, DateTimeOffset.UtcNow); - await store.AddAsync(role1); + await store.AddAsync(role1, ct: TestContext.Current.CancellationToken); - await Assert.ThrowsAsync(() => store.AddAsync(role2)); + await Assert.ThrowsAsync(() => store.AddAsync(role2, ct: TestContext.Current.CancellationToken)); } [Fact] @@ -74,21 +74,21 @@ public async Task Save_Should_Increment_Version() var store = new EfCoreRoleStore(db, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); var role = Role.Create(null, tenant, "admin", null, DateTimeOffset.UtcNow); roleId = role.Id; - await store.AddAsync(role); + await store.AddAsync(role, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) { var store = new EfCoreRoleStore(db, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - var existing = await store.GetAsync(new RoleKey(tenant, roleId)); + var existing = await store.GetAsync(new RoleKey(tenant, roleId), ct: TestContext.Current.CancellationToken); var updated = existing!.Rename("admin2", DateTimeOffset.UtcNow); - await store.SaveAsync(updated, expectedVersion: 0); + await store.SaveAsync(updated, expectedVersion: 0, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) { var store = new EfCoreRoleStore(db, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - var result = await store.GetAsync(new RoleKey(tenant, roleId)); + var result = await store.GetAsync(new RoleKey(tenant, roleId), ct: TestContext.Current.CancellationToken); Assert.Equal(1, result!.Version); Assert.Equal("admin2", result.Name); @@ -108,16 +108,16 @@ public async Task Save_With_Wrong_Version_Should_Throw() var store = new EfCoreRoleStore(db, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); var role = Role.Create(null, tenant, "admin", null, DateTimeOffset.UtcNow); roleId = role.Id; - await store.AddAsync(role); + await store.AddAsync(role, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) { var store = new EfCoreRoleStore(db, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - var existing = await store.GetAsync(new RoleKey(tenant, roleId)); + var existing = await store.GetAsync(new RoleKey(tenant, roleId), ct: TestContext.Current.CancellationToken); var updated = existing!.Rename("admin2", DateTimeOffset.UtcNow); - await Assert.ThrowsAsync(() => store.SaveAsync(updated, expectedVersion: 999)); + await Assert.ThrowsAsync(() => store.SaveAsync(updated, expectedVersion: 999, ct: TestContext.Current.CancellationToken)); } } @@ -138,17 +138,17 @@ public async Task Rename_To_Existing_Name_Should_Throw() var role2 = Role.Create(null, tenant, "user", null, DateTimeOffset.UtcNow); role1Id = role1.Id; role2Id = role2.Id; - await store.AddAsync(role1); - await store.AddAsync(role2); + await store.AddAsync(role1, ct: TestContext.Current.CancellationToken); + await store.AddAsync(role2, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) { var store = new EfCoreRoleStore(db, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - var role = await store.GetAsync(new RoleKey(tenant, role2Id)); + var role = await store.GetAsync(new RoleKey(tenant, role2Id), ct: TestContext.Current.CancellationToken); var updated = role!.Rename("admin", DateTimeOffset.UtcNow); - await Assert.ThrowsAsync(() => store.SaveAsync(updated, 0)); + await Assert.ThrowsAsync(() => store.SaveAsync(updated, 0, ct: TestContext.Current.CancellationToken)); } } @@ -173,26 +173,26 @@ public async Task Save_Should_Replace_Permissions() roleId = role.Id; - await store.AddAsync(role); + await store.AddAsync(role, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) { var store = new EfCoreRoleStore(db, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - var existing = await store.GetAsync(new RoleKey(tenant, roleId)); + var existing = await store.GetAsync(new RoleKey(tenant, roleId), ct: TestContext.Current.CancellationToken); var updated = existing!.SetPermissions( new[] { Permission.From(UAuthActions.Authorization.Roles.SetPermissionsAdmin) }, DateTimeOffset.UtcNow); - await store.SaveAsync(updated, 0); + await store.SaveAsync(updated, 0, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) { var store = new EfCoreRoleStore(db, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - var result = await store.GetAsync(new RoleKey(tenant, roleId)); + var result = await store.GetAsync(new RoleKey(tenant, roleId), ct: TestContext.Current.CancellationToken); Assert.Single(result!.Permissions); Assert.Contains(result.Permissions, p => p.Value == UAuthActions.Authorization.Roles.SetPermissionsAdmin); @@ -212,19 +212,19 @@ public async Task Soft_Delete_Should_Work() var store = new EfCoreRoleStore(db, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); var role = Role.Create(null, tenant, "admin", null, DateTimeOffset.UtcNow); roleId = role.Id; - await store.AddAsync(role); + await store.AddAsync(role, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) { var store = new EfCoreRoleStore(db, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - await store.DeleteAsync(new RoleKey(tenant, roleId), 0, DeleteMode.Soft, DateTimeOffset.UtcNow); + await store.DeleteAsync(new RoleKey(tenant, roleId), 0, DeleteMode.Soft, DateTimeOffset.UtcNow, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) { var store = new EfCoreRoleStore(db, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - var result = await store.GetAsync(new RoleKey(tenant, roleId)); + var result = await store.GetAsync(new RoleKey(tenant, roleId), ct: TestContext.Current.CancellationToken); Assert.NotNull(result!.DeletedAt); } } @@ -238,16 +238,16 @@ public async Task Query_Should_Filter_And_Page() var tenant = TenantKeys.Single; var store = new EfCoreRoleStore(db, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - await store.AddAsync(Role.Create(null, tenant, "admin", null, DateTimeOffset.UtcNow)); - await store.AddAsync(Role.Create(null, tenant, "user", null, DateTimeOffset.UtcNow)); - await store.AddAsync(Role.Create(null, tenant, "guest", null, DateTimeOffset.UtcNow)); + await store.AddAsync(Role.Create(null, tenant, "admin", null, DateTimeOffset.UtcNow), ct: TestContext.Current.CancellationToken); + await store.AddAsync(Role.Create(null, tenant, "user", null, DateTimeOffset.UtcNow), ct: TestContext.Current.CancellationToken); + await store.AddAsync(Role.Create(null, tenant, "guest", null, DateTimeOffset.UtcNow), ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync(new RoleQuery { Search = "us", PageNumber = 1, PageSize = 10 - }); + }, ct: TestContext.Current.CancellationToken); Assert.Single(result.Items); Assert.Equal("user", result.Items.First().Name); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreSessionStoreTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreSessionStoreTests.cs index dd6d81da..7c0caceb 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreSessionStoreTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreSessionStoreTests.cs @@ -64,9 +64,9 @@ await store.ExecuteAsync(async ct => await store.CreateRootAsync(root, ct); await store.CreateChainAsync(chain, ct); await store.CreateSessionAsync(session, ct); - }); + }, ct: TestContext.Current.CancellationToken); - var result = await store.GetSessionAsync(session.SessionId); + var result = await store.GetSessionAsync(session.SessionId, ct: TestContext.Current.CancellationToken); Assert.NotNull(result); Assert.Equal(session.SessionId, result!.SessionId); @@ -123,14 +123,14 @@ await store.ExecuteAsync(async ct => await store.CreateRootAsync(root, ct); await store.CreateChainAsync(chain, ct); await store.CreateSessionAsync(session, ct); - }); + }, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) { var store = new EfCoreSessionStore(db, new TenantExecutionContext(tenant)); - var result = await store.GetSessionAsync(sessionId); + var result = await store.GetSessionAsync(sessionId, ct: TestContext.Current.CancellationToken); Assert.NotNull(result); Assert.NotNull(result!.Device.DeviceId); @@ -200,14 +200,14 @@ await store.ExecuteAsync(async ct => await store.CreateRootAsync(root, ct); await store.CreateChainAsync(chain, ct); await store.CreateSessionAsync(session, ct); - }); + }, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) { var store = new EfCoreSessionStore(db, new TenantExecutionContext(tenant)); - var result = await store.GetSessionAsync(sessionId); + var result = await store.GetSessionAsync(sessionId, ct: TestContext.Current.CancellationToken); Assert.NotNull(result); @@ -269,12 +269,12 @@ await store.ExecuteAsync(async ct => await store.CreateRootAsync(root, ct); await store.CreateChainAsync(chain, ct); await store.CreateSessionAsync(session, ct); - }); + }, ct: TestContext.Current.CancellationToken); await store.ExecuteAsync(async ct => { revoked = await store.RevokeSessionAsync(sessionId, DateTimeOffset.UtcNow); - }); + }, ct: TestContext.Current.CancellationToken); Assert.True(revoked); } @@ -326,14 +326,14 @@ await store1.ExecuteAsync(async ct => await store1.CreateRootAsync(root, ct); await store1.CreateChainAsync(chain, ct); await store1.CreateSessionAsync(session, ct); - }); + }, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) { var store2 = new EfCoreSessionStore(db, new TenantExecutionContext(tenant2)); - var result = await store2.GetSessionAsync(sessionId); + var result = await store2.GetSessionAsync(sessionId, ct: TestContext.Current.CancellationToken); Assert.Null(result); } @@ -356,7 +356,7 @@ await Assert.ThrowsAsync(async () => await store.ExecuteAsync(async ct => { throw new InvalidOperationException("boom"); - }); + }, ct: TestContext.Current.CancellationToken); }); } } @@ -406,13 +406,13 @@ await store.ExecuteAsync(async ct => await store.CreateRootAsync(root, ct); await store.CreateChainAsync(chain, ct); await store.CreateSessionAsync(session, ct); - }); + }, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) { var store = new EfCoreSessionStore(db, new TenantExecutionContext(tenant)); - var sessions = await store.GetSessionsByChainAsync(chainId); + var sessions = await store.GetSessionsByChainAsync(chainId, ct: TestContext.Current.CancellationToken); Assert.Single(sessions); } } @@ -461,14 +461,14 @@ await store.ExecuteAsync(async ct => await store.CreateRootAsync(root, ct); await store.CreateChainAsync(chain, ct); await store.CreateSessionAsync(session, ct); - }); + }, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) { var store = new EfCoreSessionStore(db, new TenantExecutionContext(tenant)); - var result = await store.GetSessionAsync(sessionId); + var result = await store.GetSessionAsync(sessionId, ct: TestContext.Current.CancellationToken); Assert.NotNull(result); } @@ -498,7 +498,7 @@ await store.ExecuteAsync(async ct => // 💥 simulate failure throw new InvalidOperationException("boom"); - }); + }, ct: TestContext.Current.CancellationToken); }); } @@ -555,7 +555,7 @@ await store.ExecuteAsync(async ct => await store.CreateRootAsync(root, ct); await store.CreateChainAsync(chain, ct); await store.CreateSessionAsync(session, ct); - }); + }, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) @@ -565,13 +565,13 @@ await store.ExecuteAsync(async ct => await store.ExecuteAsync(async ct => { await store.RevokeChainCascadeAsync(chainId, DateTimeOffset.UtcNow, ct); - }); + }, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) { var store = new EfCoreSessionStore(db, new TenantExecutionContext(tenant)); - var sessions = await store.GetSessionsByChainAsync(chainId); + var sessions = await store.GetSessionsByChainAsync(chainId, ct: TestContext.Current.CancellationToken); Assert.All(sessions, s => Assert.True(s.IsRevoked)); } @@ -612,14 +612,14 @@ await store.ExecuteAsync(async ct => await store.CreateRootAsync(root, ct); await store.CreateChainAsync(chain, ct); await store.SetActiveSessionIdAsync(chainId, sessionId); - }); + }, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) { var store = new EfCoreSessionStore(db, new TenantExecutionContext(tenant)); - var active = await store.GetActiveSessionIdAsync(chainId); + var active = await store.GetActiveSessionIdAsync(chainId, ct: TestContext.Current.CancellationToken); Assert.Equal(sessionId, active); } @@ -687,7 +687,7 @@ await store.ExecuteAsync(async ct => await store.CreateRootAsync(root, ct); await store.CreateChainAsync(chain, ct); await store.CreateSessionAsync(session, ct); - }); + }, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) @@ -700,13 +700,13 @@ await store.ExecuteAsync(async ct => var updated = existing!.Revoke(DateTimeOffset.UtcNow); await store.SaveSessionAsync(updated, expectedVersion: 0, ct); - }); + }, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) { var store = new EfCoreSessionStore(db, new TenantExecutionContext(tenant)); - var result = await store.GetSessionAsync(sessionId); + var result = await store.GetSessionAsync(sessionId, ct: TestContext.Current.CancellationToken); Assert.Equal(1, result!.Version); } @@ -758,7 +758,7 @@ await store.ExecuteAsync(async ct => await store.CreateRootAsync(root, ct); await store.CreateChainAsync(chain, ct); await store.CreateSessionAsync(session, ct); - }); + }, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) @@ -773,7 +773,7 @@ await store.ExecuteAsync(async ct => var updated = existing!.Revoke(DateTimeOffset.UtcNow); await store.SaveSessionAsync(updated, expectedVersion: 999, ct); - }); + }, ct: TestContext.Current.CancellationToken); }); } } @@ -810,7 +810,7 @@ await store.ExecuteAsync(async ct => { await store.CreateRootAsync(root, ct); await store.CreateChainAsync(chain, ct); - }); + }, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) @@ -823,13 +823,13 @@ await store.ExecuteAsync(async ct => var updated = existing!.Revoke(DateTimeOffset.UtcNow); await store.SaveChainAsync(updated, expectedVersion: 0, ct); - }); + }, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) { var store = new EfCoreSessionStore(db, new TenantExecutionContext(tenant)); - var result = await store.GetChainAsync(chainId); + var result = await store.GetChainAsync(chainId, ct: TestContext.Current.CancellationToken); Assert.Equal(1, result!.Version); } @@ -861,7 +861,7 @@ public async Task SaveRoot_Should_Increment_Version() await store.ExecuteAsync(async ct => { await store.CreateRootAsync(root, ct); - }); + }, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) @@ -884,7 +884,7 @@ await store.SaveRootAsync( updated, expectedVersion: existing.Version, ct); - }); + }, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) @@ -894,7 +894,7 @@ await store.SaveRootAsync( new TenantExecutionContext(tenant)); var result = - await store.GetRootByIdAsync(rootId); + await store.GetRootByIdAsync(rootId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.Version.Should().Be(1); @@ -913,7 +913,7 @@ public async Task ExecuteAsync_Should_Join_Existing_Transaction() new TenantExecutionContext(TenantKeys.Single)); await using var transaction = - await db.Database.BeginTransactionAsync(); + await db.Database.BeginTransactionAsync(cancellationToken: TestContext.Current.CancellationToken); var originalTransaction = db.Database.CurrentTransaction; @@ -924,12 +924,13 @@ await store.ExecuteAsync(async ct => db.Database.CurrentTransaction); await Task.CompletedTask; - }); + }, ct: TestContext.Current.CancellationToken); Assert.Same( originalTransaction, db.Database.CurrentTransaction); + // Cleanup must complete even when the test is cancelled. await transaction.RollbackAsync(); } } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreTokenStoreTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreTokenStoreTests.cs index 7e36b9ff..612c7397 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreTokenStoreTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreTokenStoreTests.cs @@ -41,9 +41,9 @@ public async Task Store_And_Find_Should_Work() await store.ExecuteAsync(async ct => { await store.StoreAsync(token, ct); - }); + }, ct: TestContext.Current.CancellationToken); - var result = await store.FindByHashAsync("hash"); + var result = await store.FindByHashAsync("hash", ct: TestContext.Current.CancellationToken); Assert.NotNull(result); Assert.Equal("hash", result!.TokenHash); @@ -76,7 +76,7 @@ public async Task Revoke_Should_Set_RevokedAt() await store.ExecuteAsync(async ct => { await store.StoreAsync(token, ct); - }); + }, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) @@ -86,13 +86,13 @@ await store.ExecuteAsync(async ct => await store.ExecuteAsync(async ct => { await store.RevokeAsync(tokenHash, DateTimeOffset.UtcNow, null, ct); - }); + }, ct: TestContext.Current.CancellationToken); } await using (var db = CreateDb(connection)) { var store = new EfCoreRefreshTokenStore(db, new TenantExecutionContext(tenant)); - var result = await store.FindByHashAsync(tokenHash); + var result = await store.FindByHashAsync(tokenHash, ct: TestContext.Current.CancellationToken); Assert.NotNull(result!.RevokedAt); } @@ -119,7 +119,7 @@ public async Task Store_Outside_Transaction_Should_Throw() DateTimeOffset.UtcNow.AddHours(1) ); - await Assert.ThrowsAsync(() => store.StoreAsync(token)); + await Assert.ThrowsAsync(() => store.StoreAsync(token, ct: TestContext.Current.CancellationToken)); } [Fact] @@ -160,7 +160,7 @@ await store.StoreAsync( db, new TenantExecutionContext(tenant)); - var persisted = await store.FindByHashAsync(hash); + var persisted = await store.FindByHashAsync(hash, ct: TestContext.Current.CancellationToken); Assert.NotNull(persisted); } @@ -194,7 +194,7 @@ await store.StoreAsync( throw new InvalidOperationException( "Simulated outer transaction failure"); - })); + }, ct: TestContext.Current.CancellationToken)); }); } @@ -204,7 +204,7 @@ await store.StoreAsync( db, new TenantExecutionContext(tenant)); - var persisted = await store.FindByHashAsync(hash); + var persisted = await store.FindByHashAsync(hash, ct: TestContext.Current.CancellationToken); Assert.Null(persisted); } @@ -248,7 +248,7 @@ await store.StoreAsync( db, new TenantExecutionContext(tenant)); - Assert.NotNull(await store.FindByHashAsync(hash)); + Assert.NotNull(await store.FindByHashAsync(hash, ct: TestContext.Current.CancellationToken)); } } @@ -265,17 +265,17 @@ public async Task ExecuteAsync_AfterFailure_ShouldResetExecutionState() await Assert.ThrowsAsync( () => store.ExecuteAsync(_ => - throw new InvalidOperationException("Simulated failure"))); + throw new InvalidOperationException("Simulated failure"), ct: TestContext.Current.CancellationToken)); await store.ExecuteAsync(async ct => { await store.StoreAsync( CreateToken("after-failure", tenant), ct); - }); + }, ct: TestContext.Current.CancellationToken); Assert.NotNull( - await store.FindByHashAsync("after-failure")); + await store.FindByHashAsync("after-failure", ct: TestContext.Current.CancellationToken)); } [Fact] @@ -295,7 +295,7 @@ await Assert.ThrowsAsync( await store.ExecuteAsync( _ => Task.CompletedTask, ct); - })); + }, ct: TestContext.Current.CancellationToken)); // The failed nested call must not leave the store locked. await store.ExecuteAsync(async ct => @@ -303,10 +303,10 @@ await store.ExecuteAsync(async ct => await store.StoreAsync( CreateToken("after-nested-failure", tenant), ct); - }); + }, ct: TestContext.Current.CancellationToken); Assert.NotNull( - await store.FindByHashAsync("after-nested-failure")); + await store.FindByHashAsync("after-nested-failure", ct: TestContext.Current.CancellationToken)); } private static RefreshToken CreateToken(string hash, TenantKey tenant) diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreUserIdentifierStoreTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreUserIdentifierStoreTests.cs index f06f5f60..aa6b4f65 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreUserIdentifierStoreTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreUserIdentifierStoreTests.cs @@ -37,8 +37,8 @@ public async Task Add_And_Get_Should_Work() DateTimeOffset.UtcNow, isPrimary: true); - await store.AddAsync(identifier); - var result = await store.GetAsync(identifier.Id); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); + var result = await store.GetAsync(identifier.Id, ct: TestContext.Current.CancellationToken); Assert.NotNull(result); Assert.Equal(identifier.Id, result!.Id); @@ -63,8 +63,8 @@ public async Task Exists_Should_Return_True_When_Exists() DateTimeOffset.UtcNow, isPrimary: true); - await store.AddAsync(identifier); - var exists = await store.ExistsAsync(identifier.Id); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); + var exists = await store.ExistsAsync(identifier.Id, ct: TestContext.Current.CancellationToken); Assert.True(exists); } @@ -90,7 +90,7 @@ public async Task Save_With_Wrong_Version_Should_Throw() DateTimeOffset.UtcNow, isPrimary: true); - await store1.AddAsync(identifier); + await store1.AddAsync(identifier, ct: TestContext.Current.CancellationToken); await using var db2 = CreateDb(connection); var store2 = new EfCoreUserIdentifierStore(db2, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); @@ -98,7 +98,7 @@ public async Task Save_With_Wrong_Version_Should_Throw() var updated = identifier.SetPrimary(DateTimeOffset.UtcNow); await Assert.ThrowsAsync(() => - store2.SaveAsync(updated, expectedVersion: 999)); + store2.SaveAsync(updated, expectedVersion: 999, ct: TestContext.Current.CancellationToken)); } [Fact] @@ -122,21 +122,21 @@ public async Task Save_Should_Increment_Version() await using (var db1 = CreateDb(connection)) { var store1 = new EfCoreUserIdentifierStore(db1, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - await store1.AddAsync(identifier); + await store1.AddAsync(identifier, ct: TestContext.Current.CancellationToken); } await using (var db2 = CreateDb(connection)) { var store2 = new EfCoreUserIdentifierStore(db2, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - var existing = await store2.GetAsync(identifier.Id); + var existing = await store2.GetAsync(identifier.Id, ct: TestContext.Current.CancellationToken); var updated = existing!.SetPrimary(DateTimeOffset.UtcNow); - await store2.SaveAsync(updated, expectedVersion: 0); + await store2.SaveAsync(updated, expectedVersion: 0, ct: TestContext.Current.CancellationToken); } await using (var db3 = CreateDb(connection)) { var store3 = new EfCoreUserIdentifierStore(db3, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - var result = await store3.GetAsync(identifier.Id); + var result = await store3.GetAsync(identifier.Id, ct: TestContext.Current.CancellationToken); Assert.Equal(1, result!.Version); } } @@ -163,8 +163,8 @@ public async Task Should_Not_See_Data_From_Other_Tenant() DateTimeOffset.UtcNow, isPrimary: true); - await store1.AddAsync(identifier); - var result = await store2.GetAsync(identifier.Id); + await store1.AddAsync(identifier, ct: TestContext.Current.CancellationToken); + var result = await store2.GetAsync(identifier.Id, ct: TestContext.Current.CancellationToken); Assert.Null(result); } @@ -188,9 +188,9 @@ public async Task Soft_Delete_Should_Work() DateTimeOffset.UtcNow, isPrimary: true); - await store.AddAsync(identifier); - await store.DeleteAsync(identifier.Id, 0, DeleteMode.Soft, DateTimeOffset.UtcNow); - var result = await store.GetAsync(identifier.Id); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); + await store.DeleteAsync(identifier.Id, 0, DeleteMode.Soft, DateTimeOffset.UtcNow, ct: TestContext.Current.CancellationToken); + var result = await store.GetAsync(identifier.Id, ct: TestContext.Current.CancellationToken); Assert.NotNull(result); Assert.NotNull(result!.DeletedAt); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreUserLifecycleStoreTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreUserLifecycleStoreTests.cs index 9c97641c..8c5f0d0a 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreUserLifecycleStoreTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreUserLifecycleStoreTests.cs @@ -33,9 +33,9 @@ public async Task Add_And_Get_Should_Work() userKey, DateTimeOffset.UtcNow); - await store.AddAsync(lifecycle); + await store.AddAsync(lifecycle, ct: TestContext.Current.CancellationToken); - var result = await store.GetAsync(new UserLifecycleKey(tenant, userKey)); + var result = await store.GetAsync(new UserLifecycleKey(tenant, userKey), ct: TestContext.Current.CancellationToken); Assert.NotNull(result); Assert.Equal(userKey, result!.UserKey); @@ -58,8 +58,8 @@ public async Task Exists_Should_Return_True_When_Exists() userKey, DateTimeOffset.UtcNow); - await store.AddAsync(lifecycle); - var exists = await store.ExistsAsync(new UserLifecycleKey(tenant, userKey)); + await store.AddAsync(lifecycle, ct: TestContext.Current.CancellationToken); + var exists = await store.ExistsAsync(new UserLifecycleKey(tenant, userKey), ct: TestContext.Current.CancellationToken); Assert.True(exists); } @@ -80,21 +80,21 @@ public async Task Save_Should_Increment_Version() await using (var db1 = CreateDb(connection)) { var store1 = new EfCoreUserLifecycleStore(db1, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - await store1.AddAsync(lifecycle); + await store1.AddAsync(lifecycle, ct: TestContext.Current.CancellationToken); } await using (var db2 = CreateDb(connection)) { var store2 = new EfCoreUserLifecycleStore(db2, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - var existing = await store2.GetAsync(new UserLifecycleKey(tenant, userKey)); + var existing = await store2.GetAsync(new UserLifecycleKey(tenant, userKey), ct: TestContext.Current.CancellationToken); var updated = existing!.ChangeStatus(DateTimeOffset.UtcNow, UserStatus.Suspended); - await store2.SaveAsync(updated, expectedVersion: 0); + await store2.SaveAsync(updated, expectedVersion: 0, ct: TestContext.Current.CancellationToken); } await using (var db3 = CreateDb(connection)) { var store3 = new EfCoreUserLifecycleStore(db3, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - var result = await store3.GetAsync(new UserLifecycleKey(tenant, userKey)); + var result = await store3.GetAsync(new UserLifecycleKey(tenant, userKey), ct: TestContext.Current.CancellationToken); Assert.Equal(1, result!.Version); Assert.Equal(UserStatus.Suspended, result.Status); @@ -117,17 +117,17 @@ public async Task Save_With_Wrong_Version_Should_Throw() await using (var db1 = CreateDb(connection)) { var store1 = new EfCoreUserLifecycleStore(db1, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - await store1.AddAsync(lifecycle); + await store1.AddAsync(lifecycle, ct: TestContext.Current.CancellationToken); } await using (var db2 = CreateDb(connection)) { var store2 = new EfCoreUserLifecycleStore(db2, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - var existing = await store2.GetAsync(new UserLifecycleKey(tenant, userKey)); + var existing = await store2.GetAsync(new UserLifecycleKey(tenant, userKey), ct: TestContext.Current.CancellationToken); var updated = existing!.ChangeStatus(DateTimeOffset.UtcNow, UserStatus.Suspended); await Assert.ThrowsAsync(() => - store2.SaveAsync(updated, expectedVersion: 999)); + store2.SaveAsync(updated, expectedVersion: 999, ct: TestContext.Current.CancellationToken)); } } @@ -150,9 +150,9 @@ public async Task Should_Not_See_Data_From_Other_Tenant() userKey, DateTimeOffset.UtcNow); - await store1.AddAsync(lifecycle); + await store1.AddAsync(lifecycle, ct: TestContext.Current.CancellationToken); - var result = await store2.GetAsync(new UserLifecycleKey(tenant2, userKey)); + var result = await store2.GetAsync(new UserLifecycleKey(tenant2, userKey), ct: TestContext.Current.CancellationToken); Assert.Null(result); } @@ -173,15 +173,15 @@ public async Task Soft_Delete_Should_Work() userKey, DateTimeOffset.UtcNow); - await store.AddAsync(lifecycle); + await store.AddAsync(lifecycle, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new UserLifecycleKey(tenant, userKey), expectedVersion: 0, DeleteMode.Soft, - DateTimeOffset.UtcNow); + DateTimeOffset.UtcNow, ct: TestContext.Current.CancellationToken); - var result = await store.GetAsync(new UserLifecycleKey(tenant, userKey)); + var result = await store.GetAsync(new UserLifecycleKey(tenant, userKey), ct: TestContext.Current.CancellationToken); Assert.NotNull(result); Assert.NotNull(result!.DeletedAt); @@ -203,24 +203,24 @@ public async Task Delete_Should_Increment_SecurityVersion() await using (var db1 = CreateDb(connection)) { var store1 = new EfCoreUserLifecycleStore(db1, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - await store1.AddAsync(lifecycle); + await store1.AddAsync(lifecycle, ct: TestContext.Current.CancellationToken); } await using (var db2 = CreateDb(connection)) { var store2 = new EfCoreUserLifecycleStore(db2, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - var existing = await store2.GetAsync(new UserLifecycleKey(tenant, userKey)); + var existing = await store2.GetAsync(new UserLifecycleKey(tenant, userKey), ct: TestContext.Current.CancellationToken); var deleted = existing!.MarkDeleted(DateTimeOffset.UtcNow); - await store2.SaveAsync(deleted, expectedVersion: 0); + await store2.SaveAsync(deleted, expectedVersion: 0, ct: TestContext.Current.CancellationToken); } await using (var db3 = CreateDb(connection)) { var store3 = new EfCoreUserLifecycleStore(db3, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - var result = await store3.GetAsync(new UserLifecycleKey(tenant, userKey)); + var result = await store3.GetAsync(new UserLifecycleKey(tenant, userKey), ct: TestContext.Current.CancellationToken); Assert.Equal(1, result!.SecurityVersion); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreUserProfileStoreTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreUserProfileStoreTests.cs index 7da74d8d..0e8cba83 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreUserProfileStoreTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreUserProfileStoreTests.cs @@ -40,8 +40,8 @@ public async Task Add_And_Get_Should_Work() lastName: "last" ); - await store.AddAsync(profile); - var result = await store.GetAsync(new UserProfileKey(tenant, userKey, ProfileKey.Default)); + await store.AddAsync(profile, ct: TestContext.Current.CancellationToken); + var result = await store.GetAsync(new UserProfileKey(tenant, userKey, ProfileKey.Default), ct: TestContext.Current.CancellationToken); Assert.NotNull(result); Assert.Equal(userKey, result!.UserKey); @@ -69,8 +69,8 @@ public async Task Exists_Should_Return_True_When_Exists() lastName: "last" ); - await store.AddAsync(profile); - var exists = await store.ExistsAsync(new UserProfileKey(tenant, userKey, ProfileKey.Default)); + await store.AddAsync(profile, ct: TestContext.Current.CancellationToken); + var exists = await store.ExistsAsync(new UserProfileKey(tenant, userKey, ProfileKey.Default), ct: TestContext.Current.CancellationToken); Assert.True(exists); } @@ -97,21 +97,21 @@ public async Task Save_Should_Increment_Version() await using (var db1 = CreateDb(connection)) { var store1 = new EfCoreUserProfileStore(db1, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - await store1.AddAsync(profile); + await store1.AddAsync(profile, ct: TestContext.Current.CancellationToken); } await using (var db2 = CreateDb(connection)) { var store2 = new EfCoreUserProfileStore(db2, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - var existing = await store2.GetAsync(new UserProfileKey(tenant, userKey, ProfileKey.Default)); + var existing = await store2.GetAsync(new UserProfileKey(tenant, userKey, ProfileKey.Default), ct: TestContext.Current.CancellationToken); var updated = existing!.UpdateName(existing.FirstName, existing.LastName, "new", DateTimeOffset.UtcNow); - await store2.SaveAsync(updated, expectedVersion: 0); + await store2.SaveAsync(updated, expectedVersion: 0, ct: TestContext.Current.CancellationToken); } await using (var db3 = CreateDb(connection)) { var store3 = new EfCoreUserProfileStore(db3, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - var result = await store3.GetAsync(new UserProfileKey(tenant, userKey, ProfileKey.Default)); + var result = await store3.GetAsync(new UserProfileKey(tenant, userKey, ProfileKey.Default), ct: TestContext.Current.CancellationToken); Assert.Equal(1, result!.Version); Assert.Equal("new", result.DisplayName); @@ -140,17 +140,17 @@ public async Task Save_With_Wrong_Version_Should_Throw() await using (var db1 = CreateDb(connection)) { var store1 = new EfCoreUserProfileStore(db1, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - await store1.AddAsync(profile); + await store1.AddAsync(profile, ct: TestContext.Current.CancellationToken); } await using (var db2 = CreateDb(connection)) { var store2 = new EfCoreUserProfileStore(db2, new TenantExecutionContext(tenant), new UAuthPaginationOptions()); - var existing = await store2.GetAsync(new UserProfileKey(tenant, userKey, ProfileKey.Default)); + var existing = await store2.GetAsync(new UserProfileKey(tenant, userKey, ProfileKey.Default), ct: TestContext.Current.CancellationToken); var updated = existing!.UpdateName(existing.FirstName, existing.LastName, "new", DateTimeOffset.UtcNow); await Assert.ThrowsAsync(() => - store2.SaveAsync(updated, expectedVersion: 999)); + store2.SaveAsync(updated, expectedVersion: 999, ct: TestContext.Current.CancellationToken)); } } @@ -179,8 +179,8 @@ public async Task Should_Not_See_Data_From_Other_Tenant() lastName: "last" ); - await store1.AddAsync(profile); - var result = await store2.GetAsync(new UserProfileKey(tenant2, userKey, ProfileKey.Default)); + await store1.AddAsync(profile, ct: TestContext.Current.CancellationToken); + var result = await store2.GetAsync(new UserProfileKey(tenant2, userKey, ProfileKey.Default), ct: TestContext.Current.CancellationToken); Assert.Null(result); } @@ -207,15 +207,15 @@ public async Task Soft_Delete_Should_Work() lastName: "last" ); - await store.AddAsync(profile); + await store.AddAsync(profile, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new UserProfileKey(tenant, userKey, ProfileKey.Default), expectedVersion: 0, DeleteMode.Soft, - DateTimeOffset.UtcNow); + DateTimeOffset.UtcNow, ct: TestContext.Current.CancellationToken); - var result = await store.GetAsync(new UserProfileKey(tenant, userKey, ProfileKey.Default)); + var result = await store.GetAsync(new UserProfileKey(tenant, userKey, ProfileKey.Default), ct: TestContext.Current.CancellationToken); Assert.NotNull(result); Assert.NotNull(result!.DeletedAt); @@ -248,11 +248,11 @@ public async Task Same_User_Can_Have_Multiple_Profiles() DateTimeOffset.UtcNow, displayName: "business"); - await store.AddAsync(defaultProfile); - await store.AddAsync(businessProfile); + await store.AddAsync(defaultProfile, ct: TestContext.Current.CancellationToken); + await store.AddAsync(businessProfile, ct: TestContext.Current.CancellationToken); - var p1 = await store.GetAsync(new UserProfileKey(tenant, userKey, ProfileKey.Default)); - var p2 = await store.GetAsync(new UserProfileKey(tenant, userKey, ProfileKey.Parse("business", null))); + var p1 = await store.GetAsync(new UserProfileKey(tenant, userKey, ProfileKey.Default), ct: TestContext.Current.CancellationToken); + var p2 = await store.GetAsync(new UserProfileKey(tenant, userKey, ProfileKey.Parse("business", null)), ct: TestContext.Current.CancellationToken); Assert.NotNull(p1); Assert.NotNull(p2); @@ -276,7 +276,7 @@ await store.AddAsync(UserProfile.Create( userKey, ProfileKey.Default, DateTimeOffset.UtcNow, - displayName: "default")); + displayName: "default"), ct: TestContext.Current.CancellationToken); await store.AddAsync(UserProfile.Create( Guid.NewGuid(), @@ -284,10 +284,10 @@ await store.AddAsync(UserProfile.Create( userKey, ProfileKey.Parse("business", null), DateTimeOffset.UtcNow, - displayName: "business")); + displayName: "business"), ct: TestContext.Current.CancellationToken); var result = await store.GetAsync( - new UserProfileKey(tenant, userKey, ProfileKey.Parse("business", null))); + new UserProfileKey(tenant, userKey, ProfileKey.Parse("business", null)), ct: TestContext.Current.CancellationToken); Assert.Equal("business", result!.DisplayName); } @@ -309,7 +309,7 @@ await store.AddAsync(UserProfile.Create( userKey, ProfileKey.Default, DateTimeOffset.UtcNow, - displayName: "default")); + displayName: "default"), ct: TestContext.Current.CancellationToken); await store.AddAsync(UserProfile.Create( Guid.NewGuid(), @@ -317,11 +317,11 @@ await store.AddAsync(UserProfile.Create( userKey, ProfileKey.Parse("business", null), DateTimeOffset.UtcNow, - displayName: "business")); + displayName: "business"), ct: TestContext.Current.CancellationToken); var results = await store.GetByUsersAsync( new[] { userKey }, - ProfileKey.Default); + ProfileKey.Default, ct: TestContext.Current.CancellationToken); Assert.Single(results); Assert.Equal(ProfileKey.Default, results[0].ProfileKey); @@ -352,10 +352,10 @@ public async Task Should_Not_Allow_Duplicate_ProfileKey_For_Same_User() ProfileKey.Default, DateTimeOffset.UtcNow); - await store.AddAsync(profile1); + await store.AddAsync(profile1, ct: TestContext.Current.CancellationToken); await Assert.ThrowsAsync(() => - store.AddAsync(profile2)); + store.AddAsync(profile2, ct: TestContext.Current.CancellationToken)); } [Fact] @@ -383,20 +383,20 @@ public async Task Delete_Should_Not_Affect_Other_Profiles() ProfileKey.Parse("business", null), DateTimeOffset.UtcNow); - await store.AddAsync(defaultProfile); - await store.AddAsync(businessProfile); + await store.AddAsync(defaultProfile, ct: TestContext.Current.CancellationToken); + await store.AddAsync(businessProfile, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new UserProfileKey(tenant, userKey, ProfileKey.Default), 0, DeleteMode.Soft, - DateTimeOffset.UtcNow); + DateTimeOffset.UtcNow, ct: TestContext.Current.CancellationToken); var defaultResult = await store.GetAsync( - new UserProfileKey(tenant, userKey, ProfileKey.Default)); + new UserProfileKey(tenant, userKey, ProfileKey.Default), ct: TestContext.Current.CancellationToken); var businessResult = await store.GetAsync( - new UserProfileKey(tenant, userKey, ProfileKey.Parse("business", null))); + new UserProfileKey(tenant, userKey, ProfileKey.Parse("business", null)), ct: TestContext.Current.CancellationToken); Assert.NotNull(defaultResult!.DeletedAt); Assert.Null(businessResult!.DeletedAt); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreUserRoleStoreTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreUserRoleStoreTests.cs index 5d9b1264..212052d6 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreUserRoleStoreTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/EntityFrameworkCore/EfCoreUserRoleStoreTests.cs @@ -27,8 +27,8 @@ public async Task Assign_And_GetAssignments_Should_Work() var userKey = UserKey.FromGuid(Guid.NewGuid()); var roleId = RoleId.New(); - await store.AssignAsync(userKey, roleId, DateTimeOffset.UtcNow); - var result = await store.GetAssignmentsAsync(userKey); + await store.AssignAsync(userKey, roleId, DateTimeOffset.UtcNow, ct: TestContext.Current.CancellationToken); + var result = await store.GetAssignmentsAsync(userKey, ct: TestContext.Current.CancellationToken); Assert.Single(result); Assert.Equal(roleId, result.First().RoleId); @@ -46,9 +46,9 @@ public async Task Assign_Duplicate_Should_Throw() var userKey = UserKey.FromGuid(Guid.NewGuid()); var roleId = RoleId.New(); - await store.AssignAsync(userKey, roleId, DateTimeOffset.UtcNow); + await store.AssignAsync(userKey, roleId, DateTimeOffset.UtcNow, ct: TestContext.Current.CancellationToken); - await Assert.ThrowsAsync(() => store.AssignAsync(userKey, roleId, DateTimeOffset.UtcNow)); + await Assert.ThrowsAsync(() => store.AssignAsync(userKey, roleId, DateTimeOffset.UtcNow, ct: TestContext.Current.CancellationToken)); } [Fact] @@ -63,9 +63,9 @@ public async Task Remove_Should_Work() var userKey = UserKey.FromGuid(Guid.NewGuid()); var roleId = RoleId.New(); - await store.AssignAsync(userKey, roleId, DateTimeOffset.UtcNow); - await store.RemoveAsync(userKey, roleId); - var result = await store.GetAssignmentsAsync(userKey); + await store.AssignAsync(userKey, roleId, DateTimeOffset.UtcNow, ct: TestContext.Current.CancellationToken); + await store.RemoveAsync(userKey, roleId, ct: TestContext.Current.CancellationToken); + var result = await store.GetAssignmentsAsync(userKey, ct: TestContext.Current.CancellationToken); Assert.Empty(result); } @@ -82,7 +82,7 @@ public async Task Remove_NonExisting_Should_Not_Throw() var userKey = UserKey.FromGuid(Guid.NewGuid()); var roleId = RoleId.New(); - await store.RemoveAsync(userKey, roleId); // should not throw + await store.RemoveAsync(userKey, roleId, ct: TestContext.Current.CancellationToken); // should not throw } [Fact] @@ -96,10 +96,10 @@ public async Task CountAssignments_Should_Return_Correct_Count() var roleId = RoleId.New(); - await store.AssignAsync(UserKey.FromGuid(Guid.NewGuid()), roleId, DateTimeOffset.UtcNow); - await store.AssignAsync(UserKey.FromGuid(Guid.NewGuid()), roleId, DateTimeOffset.UtcNow); + await store.AssignAsync(UserKey.FromGuid(Guid.NewGuid()), roleId, DateTimeOffset.UtcNow, ct: TestContext.Current.CancellationToken); + await store.AssignAsync(UserKey.FromGuid(Guid.NewGuid()), roleId, DateTimeOffset.UtcNow, ct: TestContext.Current.CancellationToken); - var count = await store.CountAssignmentsAsync(roleId); + var count = await store.CountAssignmentsAsync(roleId, ct: TestContext.Current.CancellationToken); Assert.Equal(2, count); } @@ -118,12 +118,12 @@ public async Task RemoveAssignmentsByRole_Should_Remove_All() var user1 = UserKey.FromGuid(Guid.NewGuid()); var user2 = UserKey.FromGuid(Guid.NewGuid()); - await store.AssignAsync(user1, roleId, DateTimeOffset.UtcNow); - await store.AssignAsync(user2, roleId, DateTimeOffset.UtcNow); + await store.AssignAsync(user1, roleId, DateTimeOffset.UtcNow, ct: TestContext.Current.CancellationToken); + await store.AssignAsync(user2, roleId, DateTimeOffset.UtcNow, ct: TestContext.Current.CancellationToken); - await store.RemoveAssignmentsByRoleAsync(roleId); + await store.RemoveAssignmentsByRoleAsync(roleId, ct: TestContext.Current.CancellationToken); - var count = await store.CountAssignmentsAsync(roleId); + var count = await store.CountAssignmentsAsync(roleId, ct: TestContext.Current.CancellationToken); Assert.Equal(0, count); } @@ -143,9 +143,9 @@ public async Task Should_Not_See_Data_From_Other_Tenant() var userKey = UserKey.FromGuid(Guid.NewGuid()); var roleId = RoleId.New(); - await store1.AssignAsync(userKey, roleId, DateTimeOffset.UtcNow); + await store1.AssignAsync(userKey, roleId, DateTimeOffset.UtcNow, ct: TestContext.Current.CancellationToken); - var result = await store2.GetAssignmentsAsync(userKey); + var result = await store2.GetAssignmentsAsync(userKey, ct: TestContext.Current.CancellationToken); Assert.Empty(result); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/InMemory/InMemoryVersionedStoreTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/InMemory/InMemoryVersionedStoreTests.cs index 73385ead..53bf5919 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/InMemory/InMemoryVersionedStoreTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/InMemory/InMemoryVersionedStoreTests.cs @@ -15,9 +15,9 @@ public async Task AddAsync_WithoutAtomicContext_ShouldPersistEntity() var entity = TestEntity.Create("entity-1", "initial"); - await store.AddAsync(entity); + await store.AddAsync(entity, ct: TestContext.Current.CancellationToken); - var stored = await store.GetAsync("entity-1"); + var stored = await store.GetAsync("entity-1", ct: TestContext.Current.CancellationToken); stored.Should().NotBeNull(); stored!.Value.Should().Be("initial"); @@ -35,15 +35,15 @@ public async Task AddAsync_WithAtomicContext_WhenRolledBack_ShouldRemoveEntity() var store = new TestStore(accessor); await store.AddAsync( - TestEntity.Create("entity-1", "initial")); + TestEntity.Create("entity-1", "initial"), ct: TestContext.Current.CancellationToken); - (await store.ExistsAsync("entity-1")) + (await store.ExistsAsync("entity-1", ct: TestContext.Current.CancellationToken)) .Should() .BeTrue(); atomic.Rollback(); - (await store.ExistsAsync("entity-1")) + (await store.ExistsAsync("entity-1", ct: TestContext.Current.CancellationToken)) .Should() .BeFalse(); } @@ -55,27 +55,27 @@ public async Task SaveAsync_WithAtomicContext_WhenRolledBack_ShouldRestorePrevio var store = new TestStore(accessor); await store.AddAsync( - TestEntity.Create("entity-1", "initial")); + TestEntity.Create("entity-1", "initial"), ct: TestContext.Current.CancellationToken); var atomic = new InMemoryAtomicContext(); accessor.Current = atomic; - var entity = await store.GetAsync("entity-1"); + var entity = await store.GetAsync("entity-1", ct: TestContext.Current.CancellationToken); entity!.Value = "updated"; await store.SaveAsync( entity, - expectedVersion: 0); + expectedVersion: 0, ct: TestContext.Current.CancellationToken); - var updated = await store.GetAsync("entity-1"); + var updated = await store.GetAsync("entity-1", ct: TestContext.Current.CancellationToken); updated!.Value.Should().Be("updated"); updated.Version.Should().Be(1); atomic.Rollback(); - var restored = await store.GetAsync("entity-1"); + var restored = await store.GetAsync("entity-1", ct: TestContext.Current.CancellationToken); restored.Should().NotBeNull(); restored!.Value.Should().Be("initial"); @@ -89,7 +89,7 @@ public async Task DeleteAsync_Hard_WithAtomicContext_WhenRolledBack_ShouldRestor var store = new TestStore(accessor); await store.AddAsync( - TestEntity.Create("entity-1", "initial")); + TestEntity.Create("entity-1", "initial"), ct: TestContext.Current.CancellationToken); var atomic = new InMemoryAtomicContext(); accessor.Current = atomic; @@ -98,15 +98,15 @@ await store.DeleteAsync( "entity-1", expectedVersion: 0, DeleteMode.Hard, - DateTimeOffset.UtcNow); + DateTimeOffset.UtcNow, ct: TestContext.Current.CancellationToken); - (await store.ExistsAsync("entity-1")) + (await store.ExistsAsync("entity-1", ct: TestContext.Current.CancellationToken)) .Should() .BeFalse(); atomic.Rollback(); - var restored = await store.GetAsync("entity-1"); + var restored = await store.GetAsync("entity-1", ct: TestContext.Current.CancellationToken); restored.Should().NotBeNull(); restored!.Value.Should().Be("initial"); @@ -121,7 +121,7 @@ public async Task DeleteAsync_Soft_WithAtomicContext_WhenRolledBack_ShouldRestor var store = new TestStore(accessor); await store.AddAsync( - TestEntity.Create("entity-1", "initial")); + TestEntity.Create("entity-1", "initial"), ct: TestContext.Current.CancellationToken); var atomic = new InMemoryAtomicContext(); accessor.Current = atomic; @@ -136,9 +136,9 @@ await store.DeleteAsync( "entity-1", expectedVersion: 0, DeleteMode.Soft, - deletedAt); + deletedAt, ct: TestContext.Current.CancellationToken); - var deleted = await store.GetAsync("entity-1"); + var deleted = await store.GetAsync("entity-1", ct: TestContext.Current.CancellationToken); deleted.Should().NotBeNull(); deleted!.IsDeleted.Should().BeTrue(); @@ -147,7 +147,7 @@ await store.DeleteAsync( atomic.Rollback(); - var restored = await store.GetAsync("entity-1"); + var restored = await store.GetAsync("entity-1", ct: TestContext.Current.CancellationToken); restored.Should().NotBeNull(); restored!.IsDeleted.Should().BeFalse(); @@ -162,41 +162,41 @@ public async Task MultipleOperations_WhenRolledBack_ShouldRestoreOriginalState() var store = new TestStore(accessor); await store.AddAsync( - TestEntity.Create("existing", "original")); + TestEntity.Create("existing", "original"), ct: TestContext.Current.CancellationToken); var atomic = new InMemoryAtomicContext(); accessor.Current = atomic; // ADD await store.AddAsync( - TestEntity.Create("new", "created")); + TestEntity.Create("new", "created"), ct: TestContext.Current.CancellationToken); // UPDATE - var existing = await store.GetAsync("existing"); + var existing = await store.GetAsync("existing", ct: TestContext.Current.CancellationToken); existing!.Value = "updated"; await store.SaveAsync( existing, - expectedVersion: 0); + expectedVersion: 0, ct: TestContext.Current.CancellationToken); // DELETE newly-created entity await store.DeleteAsync( "new", expectedVersion: 0, DeleteMode.Hard, - DateTimeOffset.UtcNow); + DateTimeOffset.UtcNow, ct: TestContext.Current.CancellationToken); atomic.Rollback(); var restoredExisting = - await store.GetAsync("existing"); + await store.GetAsync("existing", ct: TestContext.Current.CancellationToken); restoredExisting.Should().NotBeNull(); restoredExisting!.Value.Should().Be("original"); restoredExisting.Version.Should().Be(0); - (await store.ExistsAsync("new")) + (await store.ExistsAsync("new", ct: TestContext.Current.CancellationToken)) .Should() .BeFalse(); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Policies/AccessPoliciesTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Policies/AccessPoliciesTests.cs index 4889b9a5..cfb317dd 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Policies/AccessPoliciesTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Policies/AccessPoliciesTests.cs @@ -44,7 +44,7 @@ public async Task Decide_UnauthenticatedActor_ShouldDeny() var context = TestAccessContext.WithAction("users.get.self"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeFalse(); result.DenyReason.Should().Be("unauthenticated"); @@ -59,7 +59,7 @@ public async Task Decide_AuthenticatedActor_ShouldAllow() UserKey.New(), "users.get.self"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeTrue(); } @@ -85,7 +85,7 @@ public async Task Decide_SameTenant_ShouldAllow() var context = TestAccessContext.ForUser(UserKey.New(), "users.get.self"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeTrue(); } @@ -112,7 +112,7 @@ public async Task Decide_CrossTenant_ShouldDeny() clientProfile: UAuthClientProfile.NotSpecified, attributes: EmptyAttributes.Instance); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeFalse(); result.DenyReason.Should().Be("cross_tenant_access_denied"); @@ -153,7 +153,7 @@ public async Task Decide_UnauthenticatedActor_ShouldDeny() var context = TestAccessContext.WithAction("users.update.self"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeFalse(); result.DenyReason.Should().Be("unauthenticated"); @@ -171,7 +171,7 @@ public async Task Decide_WhenActorIsTarget_ShouldAllow() userKey, "users.update.self"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeTrue(); result.DenyReason.Should().BeNull(); @@ -191,7 +191,7 @@ public async Task Decide_WhenActorIsNotTarget_ShouldDeny() target, "users.update.self"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeFalse(); result.DenyReason.Should().Be("not_self"); @@ -232,7 +232,7 @@ public async Task Decide_NormalActor_ShouldDeny() var context = TestAccessContext.WithAction("users.repair.system"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeFalse(); result.DenyReason.Should().Be("system_actor_required"); @@ -257,7 +257,7 @@ public async Task Decide_SystemActor_ShouldAllow() clientProfile: UAuthClientProfile.NotSpecified, attributes: EmptyAttributes.Instance); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeTrue(); result.DenyReason.Should().BeNull(); @@ -344,7 +344,7 @@ public async Task Decide_UnauthenticatedActor_ShouldDeny() var context = TestAccessContext.WithAction("users.update.admin"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeFalse(); result.DenyReason.Should().Be("unauthenticated"); @@ -363,7 +363,7 @@ public async Task Decide_AdminModifyingOwnAccount_ShouldDeny() userKey, "users.update.admin"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeFalse(); result.DenyReason.Should() @@ -383,7 +383,7 @@ public async Task Decide_AdminDeletingOwnAccount_ShouldDeny() userKey, "users.delete.admin"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeFalse(); result.DenyReason.Should() @@ -404,7 +404,7 @@ public async Task Decide_AdminModifyingDifferentUser_ShouldAllow() target, "users.update.admin"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeTrue(); result.DenyReason.Should().BeNull(); @@ -511,7 +511,7 @@ public async Task Decide_ShouldDelegateToInnerPolicy() var context = TestAccessContext.WithAction("users.get.self"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeFalse(); result.DenyReason.Should().Be("inner_denied"); @@ -534,7 +534,7 @@ public async Task Decide_ShouldReturnInnerAllowDecision() var context = TestAccessContext.WithAction("users.get.self"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeTrue(); inner.DecideCallCount.Should().Be(1); @@ -619,7 +619,7 @@ public async Task Decide_WhenPermissionsAttributeIsMissing_ShouldDeny() var context = TestAccessContext.WithAction("users.update.admin"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeFalse(); result.DenyReason.Should().Be("missing_permission"); @@ -632,7 +632,7 @@ public async Task Decide_WhenPermissionsAttributeHasWrongType_ShouldDeny() var context = TestAccessContext.WithAction("users.update.admin") .WithAttribute(UAuthConstants.Access.Permissions, "invalid-permissions"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeFalse(); result.DenyReason.Should().Be("missing_permission"); @@ -654,7 +654,7 @@ public async Task Decide_WhenPermissionAllowsAction_ShouldAllow() UAuthConstants.Access.Permissions, permissions); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeTrue(); } @@ -674,7 +674,7 @@ public async Task Decide_WhenPermissionDoesNotAllowAction_ShouldDeny() UAuthConstants.Access.Permissions, permissions); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeFalse(); result.DenyReason.Should().Be("missing_permission"); @@ -695,7 +695,7 @@ public async Task Decide_WhenPermissionAllowsExactAction_ShouldAllow() UAuthConstants.Access.Permissions, permissions); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeTrue(); result.DenyReason.Should().BeNull(); @@ -781,7 +781,7 @@ public async Task Decide_WhenActorIsMissing_ShouldDeny() var context = TestAccessContext.WithAction("users.get.self"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeFalse(); result.DenyReason.Should().Be("missing_actor"); @@ -801,7 +801,7 @@ public async Task Decide_WhenRuntimeStateIsMissing_ShouldDeny() var context = TestAccessContext.ForUser(UserKey.New(), "users.get.self"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeFalse(); result.DenyReason.Should().Be("user_not_found"); @@ -822,7 +822,7 @@ public async Task Decide_WhenUserDoesNotExist_ShouldDeny() var context = TestAccessContext.ForUser(UserKey.New(), "users.get.self"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeFalse(); result.DenyReason.Should().Be("user_not_found"); @@ -845,7 +845,7 @@ public async Task Decide_WhenUserIsDeleted_ShouldDeny() UserKey.New(), "users.get.self"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeFalse(); result.DenyReason.Should().Be("user_not_found"); @@ -868,7 +868,7 @@ public async Task Decide_WhenUserIsInactive_ShouldDeny() UserKey.New(), "users.get.self"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeFalse(); result.DenyReason.Should().Be("user_not_active"); @@ -891,7 +891,7 @@ public async Task Decide_WhenUserIsActive_ShouldAllow() UserKey.New(), "users.get.self"); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); result.IsAllowed.Should().BeTrue(); result.DenyReason.Should().BeNull(); @@ -915,7 +915,7 @@ public async Task Decide_ShouldQueryRuntimeUsingActorTenantAndUserKey() var sut = new RequireActiveUserPolicy(runtime); var context = TestAccessContext.ForUser(userKey, "users.get.self", tenant); - var result = await sut.DecideAsync(context); + var result = await sut.DecideAsync(context, ct: TestContext.Current.CancellationToken); runtime.LastTenant.Should().Be(tenant); runtime.LastUserKey.Should().Be(userKey); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/ResourceApi/RemoteSessionValidatorTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/ResourceApi/RemoteSessionValidatorTests.cs index 2b184062..040f6718 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/ResourceApi/RemoteSessionValidatorTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/ResourceApi/RemoteSessionValidatorTests.cs @@ -32,7 +32,7 @@ public async Task ValidateSessionAsync_ShouldPostSessionAndTenantToValidationEnd var context = CreateValidationContext(); - await sut.ValidateSessionAsync(context); + await sut.ValidateSessionAsync(context, ct: TestContext.Current.CancellationToken); handler.Method.Should().Be(HttpMethod.Post); @@ -74,7 +74,7 @@ public async Task ValidateSessionAsync_WhenIncomingRequestContainsCookie_ShouldF httpContext); await sut.ValidateSessionAsync( - CreateValidationContext()); + CreateValidationContext(), ct: TestContext.Current.CancellationToken); handler.Cookie.Should() .Be("uauth_session=session-cookie; other=value"); @@ -91,7 +91,7 @@ public async Task ValidateSessionAsync_WhenIncomingRequestDoesNotContainCookie_S var sut = CreateSut(handler); await sut.ValidateSessionAsync( - CreateValidationContext()); + CreateValidationContext(), ct: TestContext.Current.CancellationToken); handler.Cookie.Should().BeNull(); } @@ -116,7 +116,7 @@ public async Task ValidateSessionAsync_WhenHubReturnsNonSuccess_ShouldReturnNotF var context = CreateValidationContext(); var result = - await sut.ValidateSessionAsync(context); + await sut.ValidateSessionAsync(context, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.State.Should().Be(SessionState.NotFound); @@ -141,7 +141,7 @@ public async Task ValidateSessionAsync_WhenHubReturnsJsonNull_ShouldReturnNotFou var context = CreateValidationContext(); var result = - await sut.ValidateSessionAsync(context); + await sut.ValidateSessionAsync(context, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.State.Should().Be(SessionState.NotFound); @@ -171,7 +171,7 @@ public async Task ValidateSessionAsync_WhenHubReturnsInvalidResult_ShouldMapResu var result = await sut.ValidateSessionAsync( - CreateValidationContext()); + CreateValidationContext(), ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.State.Should().Be(SessionState.Revoked); @@ -249,7 +249,7 @@ public async Task ValidateSessionAsync_WhenHubReturnsValidSnapshot_ShouldReturnA var sut = CreateSut(handler); var result = await sut.ValidateSessionAsync( - CreateValidationContext()); + CreateValidationContext(), ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeTrue(); result.State.Should().Be(SessionState.Active); @@ -294,7 +294,7 @@ public async Task ValidateSessionAsync_WhenHubReturnsValidWithoutIdentity_Should var sut = CreateSut(handler); var result = await sut.ValidateSessionAsync( - CreateValidationContext()); + CreateValidationContext(), ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); } @@ -322,7 +322,7 @@ public async Task ValidateSessionAsync_WhenHubReturnsValidWithoutSnapshot_Should var sut = CreateSut(handler); var result = await sut.ValidateSessionAsync( - CreateValidationContext()); + CreateValidationContext(), ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/AccessContextFactoryTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/AccessContextFactoryTests.cs index 4f627fb1..38023698 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/AccessContextFactoryTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/AccessContextFactoryTests.cs @@ -89,7 +89,7 @@ public async Task CreateAsync_ShouldResolveTrustedActorRoles() var context = await sut.CreateAsync( flow, "users.get.self", - "users"); + "users", ct: TestContext.Current.CancellationToken); // Only active roles are exposed. context.ActorRoles.Should().HaveCount(2); @@ -161,7 +161,7 @@ public async Task CreateAsync_ShouldIgnoreUntrustedRolesForAnonymousActor() flow, "users.get.anonymous", "users", - attributes: attributes); + attributes: attributes, ct: TestContext.Current.CancellationToken); // Anonymous actors must never receive trusted roles. context.ActorRoles.Should().BeEmpty(); @@ -203,7 +203,7 @@ public async Task CreateForUserTargetsAsync_ShouldNormalizeAndPreserveTargets() flow, "users.getbykeys.admin", "users", - new[] { first, second, first }); + new[] { first, second, first }, ct: TestContext.Current.CancellationToken); context.TargetUserKeys.Should().HaveCount(2); context.TargetUserKeys.Should().ContainInOrder(first, second); @@ -236,7 +236,7 @@ public async Task CreateForUserTargetsAsync_ShouldSnapshotInputCollection() flow, "users.getbykeys.admin", "users", - input); + input, ct: TestContext.Current.CancellationToken); input.Add(second); @@ -263,7 +263,7 @@ public async Task CreateForUserTargetsAsync_WithAttribute_ShouldPreserveTargets( flow, "users.getbykeys.admin", "users", - new[] { first, second }); + new[] { first, second }, ct: TestContext.Current.CancellationToken); var updated = context.WithAttribute("source", "test"); @@ -289,7 +289,7 @@ public async Task CreateAsync_SingleTargetContext_ShouldHaveEmptyTargetUserKeys( var context = await sut.CreateAsync( flow, "users.query.admin", - "users"); + "users", ct: TestContext.Current.CancellationToken); context.TargetUserKeys.Should().BeEmpty(); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/AccessPipelineTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/AccessPipelineTests.cs index b7ffd6e5..37160702 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/AccessPipelineTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/AccessPipelineTests.cs @@ -30,7 +30,7 @@ public async Task Allowed_pipeline_should_execute_command_once() await pipeline.Orchestrator.ExecuteAsync( TestAccessContext.WithAction("users.read"), - command); + command, ct: TestContext.Current.CancellationToken); executions.Should().Be(1); } @@ -49,7 +49,7 @@ public async Task Generic_pipeline_should_return_command_result() var result = await pipeline.Orchestrator.ExecuteAsync( TestAccessContext.WithAction("users.read"), - command); + command, ct: TestContext.Current.CancellationToken); result.Should().Be("expected-result"); executions.Should().Be(1); @@ -169,7 +169,7 @@ await pipeline.Orchestrator.ExecuteAsync( { executed = true; return Task.CompletedTask; - })); + }), ct: TestContext.Current.CancellationToken); executed.Should().BeTrue(); @@ -317,7 +317,7 @@ public async Task Runtime_reauthentication_should_be_reported_by_authority() var decision = await authority.DecideAsync( TestAccessContext.WithAction("users.update"), - new[] { first.Object, second.Object }); + new[] { first.Object, second.Object }, ct: TestContext.Current.CancellationToken); decision.RequiresReauthentication.Should().BeTrue(); decision.IsAllowed.Should().BeFalse(); @@ -340,7 +340,7 @@ public async Task Runtime_denial_should_take_priority_over_earlier_reauthenticat var decision = await authority.DecideAsync( TestAccessContext.WithAction("users.update"), - new[] { reauth.Object, deny.Object }); + new[] { reauth.Object, deny.Object }, ct: TestContext.Current.CancellationToken); decision.IsDenied.Should().BeTrue(); decision.DenyReason.Should().Be("explicit_denial"); @@ -397,7 +397,7 @@ public async Task Authenticated_actor_should_receive_compiled_permissions() await pipeline.Orchestrator.ExecuteAsync( context, - new AccessCommand(_ => Task.CompletedTask)); + new AccessCommand(_ => Task.CompletedTask), ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); captured.Should().NotBeSameAs(context); @@ -454,7 +454,7 @@ public async Task Authority_should_receive_same_enriched_context_as_policy_provi await pipeline.Orchestrator.ExecuteAsync( context, - new AccessCommand(_ => Task.CompletedTask)); + new AccessCommand(_ => Task.CompletedTask), ct: TestContext.Current.CancellationToken); providerContext.Should().NotBeNull(); authorityContext.Should().BeSameAs(providerContext); @@ -476,7 +476,7 @@ public async Task Anonymous_actor_should_skip_permission_store() await pipeline.Orchestrator.ExecuteAsync( context, - new AccessCommand(_ => Task.CompletedTask)); + new AccessCommand(_ => Task.CompletedTask), ct: TestContext.Current.CancellationToken); pipeline.PermissionStore.Verify( s => s.GetPermissionsAsync( @@ -615,7 +615,7 @@ public async Task Authority_should_allow_when_no_policies_exist() var decision = await authority.DecideAsync( TestAccessContext.WithAction("users.delete"), - Array.Empty()); + Array.Empty(), ct: TestContext.Current.CancellationToken); decision.IsAllowed.Should().BeTrue(); } @@ -629,7 +629,7 @@ public async Task Authority_should_treat_null_global_collections_as_empty() var decision = await authority.DecideAsync( TestAccessContext.WithAction("users.read"), - Array.Empty()); + Array.Empty(), ct: TestContext.Current.CancellationToken); decision.IsAllowed.Should().BeTrue(); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/AuthAuthorityTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/AuthAuthorityTests.cs index d6c0eaba..fe29c217 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/AuthAuthorityTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/AuthAuthorityTests.cs @@ -16,7 +16,7 @@ public async Task DecideAsync_WithLegacyInvariant_ShouldWork() new IAuthorityInvariant[] { invariant }, Array.Empty()); - var result = await authority.DecideAsync(CreateContext()); + var result = await authority.DecideAsync(CreateContext(), ct: TestContext.Current.CancellationToken); result.Decision.Should().Be(AuthorizationDecision.Deny); result.Reason.Should().Be("legacy_invariant_denied"); @@ -32,7 +32,7 @@ public async Task DecideAsync_ShouldAwaitAsyncPolicy() Array.Empty(), new IAuthorityPolicy[] { policy }); - var evaluation = authority.DecideAsync(CreateContext()); + var evaluation = authority.DecideAsync(CreateContext(), ct: TestContext.Current.CancellationToken); // Ensure the policy has started evaluating. await policy.Started; diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/AuthenticationSecurityManagerTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/AuthenticationSecurityManagerTests.cs index 2e7664b9..af993204 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/AuthenticationSecurityManagerTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/AuthenticationSecurityManagerTests.cs @@ -60,7 +60,7 @@ public async Task GetOrCreateAccountAsync_WhenStateExists_ShouldReturnExistingSt var result = await _sut.GetOrCreateAccountAsync( _tenant, - _userKey); + _userKey, ct: TestContext.Current.CancellationToken); result.Should().BeSameAs(existing); @@ -94,7 +94,7 @@ public async Task GetOrCreateAccountAsync_WhenStateDoesNotExist_ShouldCreateStat var result = await _sut.GetOrCreateAccountAsync( _tenant, - _userKey); + _userKey, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result.Tenant.Should().Be(_tenant); @@ -139,7 +139,7 @@ public async Task GetOrCreateAccountAsync_WhenConcurrentCreationOccurs_ShouldRet var result = await _sut.GetOrCreateAccountAsync( _tenant, - _userKey); + _userKey, ct: TestContext.Current.CancellationToken); result.Should().BeSameAs(existing); @@ -203,7 +203,7 @@ public async Task GetOrCreateFactorAsync_WhenStateExists_ShouldReturnExistingSta var result = await _sut.GetOrCreateFactorAsync( _tenant, _userKey, - CredentialType.Password); + CredentialType.Password, ct: TestContext.Current.CancellationToken); result.Should().BeSameAs(existing); @@ -238,7 +238,7 @@ public async Task GetOrCreateFactorAsync_WhenStateDoesNotExist_ShouldCreateCorre var result = await _sut.GetOrCreateFactorAsync( _tenant, _userKey, - CredentialType.Password); + CredentialType.Password, ct: TestContext.Current.CancellationToken); result.Tenant.Should().Be(_tenant); result.UserKey.Should().Be(_userKey); @@ -287,7 +287,7 @@ public async Task GetOrCreateFactorAsync_WhenConcurrentCreationOccurs_ShouldRetu var result = await _sut.GetOrCreateFactorAsync( _tenant, _userKey, - CredentialType.Password); + CredentialType.Password, ct: TestContext.Current.CancellationToken); result.Should().BeSameAs(existing); } @@ -315,7 +315,7 @@ public async Task MutateAccountAsync_WhenMutationReturnsSameInstance_ShouldNotUp var result = await _sut.MutateAccountAsync( _tenant, _userKey, - state => state); + state => state, ct: TestContext.Current.CancellationToken); result.Should().BeSameAs(existing); @@ -355,7 +355,7 @@ public async Task MutateAccountAsync_WhenUpdateSucceeds_ShouldUseCurrentSecurity var result = await _sut.MutateAccountAsync( _tenant, _userKey, - _ => updated); + _ => updated, ct: TestContext.Current.CancellationToken); result.Should().BeSameAs(updated); @@ -425,7 +425,7 @@ public async Task MutateAccountAsync_WhenUpdateConflicts_ShouldReloadAndReapplyM { mutationCall++; return CreateMutatedState(state); - }); + }, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -506,7 +506,7 @@ public async Task MutateFactorAsync_WhenMutationReturnsSameInstance_ShouldNotUpd _tenant, _userKey, CredentialType.Password, - state => state); + state => state, ct: TestContext.Current.CancellationToken); result.Should().BeSameAs(existing); @@ -568,7 +568,7 @@ public async Task MutateFactorAsync_WhenUpdateConflicts_ShouldReloadAndReapplyMu { mutationCalls++; return CreateMutatedState(state); - }); + }, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -601,7 +601,7 @@ public async Task UpdateAsync_ShouldResolveStoreUsingUpdatedStateTenant() await _sut.UpdateAsync( state, - expectedVersion: 7); + expectedVersion: 7, ct: TestContext.Current.CancellationToken); _storeFactory.Verify( x => x.Create(otherTenant), @@ -632,7 +632,7 @@ await _sut.DeleteAsync( otherTenant, _userKey, AuthenticationSecurityScope.Factor, - CredentialType.Password); + CredentialType.Password, ct: TestContext.Current.CancellationToken); _storeFactory.Verify( x => x.Create(otherTenant), diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/HubCredentialResolverTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/HubCredentialResolverTests.cs index 98ad80d4..807bc3e1 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/HubCredentialResolverTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/HubCredentialResolverTests.cs @@ -27,7 +27,7 @@ public async Task ResolveAsync_WhenArtifactDoesNotExist_ShouldReturnNull() It.IsAny())) .ReturnsAsync((AuthArtifact?)null); - var result = await fixture.Sut.ResolveAsync(hubSessionId); + var result = await fixture.Sut.ResolveAsync(hubSessionId, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -47,7 +47,7 @@ public async Task ResolveAsync_WhenArtifactIsNotHubFlow_ShouldReturnNull() It.IsAny())) .ReturnsAsync(artifact); - var result = await fixture.Sut.ResolveAsync(hubSessionId); + var result = await fixture.Sut.ResolveAsync(hubSessionId, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -63,7 +63,7 @@ public async Task ResolveAsync_WhenFlowIsCompleted_ShouldReturnNull() SetupArtifact(fixture, artifact); var result = await fixture.Sut.ResolveAsync( - artifact.HubSessionId); + artifact.HubSessionId, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -81,7 +81,7 @@ public async Task ResolveAsync_WhenAuthorizationCodeIsMissing_ShouldReturnNull() SetupArtifact(fixture, artifact); var result = await fixture.Sut.ResolveAsync( - artifact.HubSessionId); + artifact.HubSessionId, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -100,7 +100,7 @@ public async Task ResolveAsync_WhenAuthorizationCodeIsEmpty_ShouldReturnNull() SetupArtifact(fixture, artifact); var result = await fixture.Sut.ResolveAsync( - artifact.HubSessionId); + artifact.HubSessionId, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -118,7 +118,7 @@ public async Task ResolveAsync_WhenCodeVerifierIsMissing_ShouldReturnNull() SetupArtifact(fixture, artifact); var result = await fixture.Sut.ResolveAsync( - artifact.HubSessionId); + artifact.HubSessionId, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -137,7 +137,7 @@ public async Task ResolveAsync_WhenCodeVerifierIsEmpty_ShouldReturnNull() SetupArtifact(fixture, artifact); var result = await fixture.Sut.ResolveAsync( - artifact.HubSessionId); + artifact.HubSessionId, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -153,7 +153,7 @@ public async Task ResolveAsync_WhenCredentialsAreValid_ShouldReturnCredentials() SetupArtifact(fixture, artifact); var result = await fixture.Sut.ResolveAsync( - artifact.HubSessionId); + artifact.HubSessionId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -171,7 +171,7 @@ public async Task ResolveAsync_ShouldReadArtifactUsingHubSessionId() SetupArtifact(fixture, artifact); await fixture.Sut.ResolveAsync( - artifact.HubSessionId); + artifact.HubSessionId, ct: TestContext.Current.CancellationToken); fixture.Store.Verify( x => x.GetAsync( @@ -218,7 +218,7 @@ public async Task ResolveAsync_WhenFlowIsExpired_ShouldReturnNull() SetupArtifact(fixture, artifact); var result = await fixture.Sut.ResolveAsync( - artifact.HubSessionId); + artifact.HubSessionId, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -234,7 +234,7 @@ public async Task ResolveAsync_WhenFlowExpiresExactlyNow_ShouldReturnNull() SetupArtifact(fixture, artifact); var result = await fixture.Sut.ResolveAsync( - artifact.HubSessionId); + artifact.HubSessionId, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/HubFlowReaderTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/HubFlowReaderTests.cs index a32b437e..f891464d 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/HubFlowReaderTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/HubFlowReaderTests.cs @@ -34,7 +34,7 @@ public async Task GetStateAsync_WhenArtifactDoesNotExist_ShouldReturnNull() It.IsAny())) .ReturnsAsync((AuthArtifact?)null); - var result = await fixture.Sut.GetStateAsync(hubSessionId); + var result = await fixture.Sut.GetStateAsync(hubSessionId, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -54,7 +54,7 @@ public async Task GetStateAsync_WhenArtifactIsNotHubFlow_ShouldReturnNull() new TestAuthArtifact( Now.AddMinutes(5))); - var result = await fixture.Sut.GetStateAsync(hubSessionId); + var result = await fixture.Sut.GetStateAsync(hubSessionId, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -74,7 +74,7 @@ public async Task GetStateAsync_WhenFlowIsActive_ShouldReturnActiveState() SetupArtifact(fixture, artifact); var result = await fixture.Sut.GetStateAsync( - artifact.HubSessionId); + artifact.HubSessionId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -99,7 +99,7 @@ public async Task GetStateAsync_WhenFlowIsExpired_ShouldReturnExpiredState() SetupArtifact(fixture, artifact); var result = await fixture.Sut.GetStateAsync( - artifact.HubSessionId); + artifact.HubSessionId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -120,7 +120,7 @@ public async Task GetStateAsync_WhenFlowExpiresExactlyNow_ShouldReturnExpiredSta SetupArtifact(fixture, artifact); var result = await fixture.Sut.GetStateAsync( - artifact.HubSessionId); + artifact.HubSessionId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -147,7 +147,7 @@ public async Task GetStateAsync_WhenFlowIsCompleted_ShouldReturnCompletedState() SetupArtifact(fixture, artifact); var result = await fixture.Sut.GetStateAsync( - artifact.HubSessionId); + artifact.HubSessionId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -170,7 +170,7 @@ public async Task GetStateAsync_WhenFlowIsExpiredAndCompleted_ShouldPreserveBoth SetupArtifact(fixture, artifact); var result = await fixture.Sut.GetStateAsync( - artifact.HubSessionId); + artifact.HubSessionId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -197,7 +197,7 @@ public async Task GetStateAsync_ShouldMapHubFlowState() SetupArtifact(fixture, artifact); var result = await fixture.Sut.GetStateAsync( - artifact.HubSessionId); + artifact.HubSessionId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -234,7 +234,7 @@ public async Task GetStateAsync_ShouldMapErrorAndAttemptCount() SetupArtifact(fixture, artifact); var result = await fixture.Sut.GetStateAsync( - artifact.HubSessionId); + artifact.HubSessionId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -260,7 +260,7 @@ public async Task GetStateAsync_ShouldPreserveMultipleAttempts() SetupArtifact(fixture, artifact); var result = await fixture.Sut.GetStateAsync( - artifact.HubSessionId); + artifact.HubSessionId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -282,7 +282,7 @@ public async Task GetStateAsync_ShouldReadArtifactUsingHubSessionId() SetupArtifact(fixture, artifact); await fixture.Sut.GetStateAsync( - artifact.HubSessionId); + artifact.HubSessionId, ct: TestContext.Current.CancellationToken); fixture.Store.Verify( x => x.GetAsync( diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/HubFlowServiceTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/HubFlowServiceTests.cs index 41fe0264..334623f6 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/HubFlowServiceTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/HubFlowServiceTests.cs @@ -53,7 +53,7 @@ public async Task BeginLoginAsync_ShouldCreateAndStoreHubFlowArtifact() }) .Returns(Task.CompletedTask); - var result = await fixture.Sut.BeginLoginAsync(request); + var result = await fixture.Sut.BeginLoginAsync(request, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result.HubSessionId.Should().NotBeNullOrWhiteSpace(); @@ -96,7 +96,7 @@ public async Task BeginLoginAsync_ShouldStoreAuthorizationCodeAndCodeVerifier() .Returns(Task.CompletedTask); await fixture.Sut.BeginLoginAsync( - CreateBeginRequest()); + CreateBeginRequest(), ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); @@ -129,7 +129,7 @@ public async Task BeginLoginAsync_ShouldSetExpirationFromClockAndConfiguredLifet .Returns(Task.CompletedTask); await fixture.Sut.BeginLoginAsync( - CreateBeginRequest()); + CreateBeginRequest(), ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); captured!.ExpiresAt.Should().Be(Now.Add(FlowLifetime)); @@ -145,7 +145,7 @@ public async Task BeginLoginAsync_WhenPreviousHubSessionIdExists_ShouldConsumePr var request = CreateBeginRequest( previousHubSessionId: previousId); - await fixture.Sut.BeginLoginAsync(request); + await fixture.Sut.BeginLoginAsync(request, ct: TestContext.Current.CancellationToken); fixture.Store.Verify( x => x.ConsumeAsync( @@ -167,7 +167,7 @@ public async Task BeginLoginAsync_WhenPreviousHubSessionIdIsMissing_ShouldNotCon var request = CreateBeginRequest( previousHubSessionId: previousHubSessionId); - await fixture.Sut.BeginLoginAsync(request); + await fixture.Sut.BeginLoginAsync(request, ct: TestContext.Current.CancellationToken); fixture.Store.Verify( x => x.ConsumeAsync( @@ -234,7 +234,7 @@ public async Task ContinuePkceAsync_ShouldReplacePkceCredentials() await fixture.Sut.ContinuePkceAsync( artifact.HubSessionId.Value, newAuthorizationCode, - newCodeVerifier); + newCodeVerifier, ct: TestContext.Current.CancellationToken); artifact.Payload .GetRequired("authorization_code") @@ -264,7 +264,7 @@ public async Task ContinuePkceAsync_ShouldClearExistingError() await fixture.Sut.ContinuePkceAsync( artifact.HubSessionId.Value, "new-code", - "new-verifier"); + "new-verifier", ct: TestContext.Current.CancellationToken); artifact.Error.Should().BeNull(); @@ -285,7 +285,7 @@ public async Task ContinuePkceAsync_ShouldStoreUpdatedArtifact() await fixture.Sut.ContinuePkceAsync( artifact.HubSessionId.Value, "new-code", - "new-verifier"); + "new-verifier", ct: TestContext.Current.CancellationToken); fixture.Store.Verify( x => x.StoreAsync( @@ -527,7 +527,7 @@ public async Task ConsumeAsync_WhenHubSessionIdIsEmpty_ShouldDoNothing( { var fixture = CreateFixture(); - await fixture.Sut.ConsumeAsync(hubSessionId); + await fixture.Sut.ConsumeAsync(hubSessionId, ct: TestContext.Current.CancellationToken); fixture.Store.Verify( x => x.ConsumeAsync( @@ -543,7 +543,7 @@ public async Task ConsumeAsync_WhenHubSessionIdExists_ShouldConsumeArtifact() const string hubSessionId = "hub-session"; - await fixture.Sut.ConsumeAsync(hubSessionId); + await fixture.Sut.ConsumeAsync(hubSessionId, ct: TestContext.Current.CancellationToken); fixture.Store.Verify( x => x.ConsumeAsync( diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/LoginOrchestratorTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/LoginOrchestratorTests.cs index 3402774d..058f45fe 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/LoginOrchestratorTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/LoginOrchestratorTests.cs @@ -29,7 +29,7 @@ public async Task Successful_login_should_return_success_result() { Identifier = "user", Secret = "user", - }); + }, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); } @@ -46,7 +46,7 @@ public async Task Successful_login_should_create_session() { Identifier = "user", Secret = "user", - }); + }, ct: TestContext.Current.CancellationToken); result.SessionId.Should().NotBeNull(); } @@ -67,11 +67,11 @@ await orchestrator.LoginAsync(flow, { Identifier = "user", Secret = "wrong", - }); + }, ct: TestContext.Current.CancellationToken); var factory = runtime.Services.GetRequiredService(); var store = factory.Create(TenantKeys.Single); - var state = await store.GetAsync(TestUsers.User, AuthenticationSecurityScope.Factor, CredentialType.Password); + var state = await store.GetAsync(TestUsers.User, AuthenticationSecurityScope.Factor, CredentialType.Password, ct: TestContext.Current.CancellationToken); state?.FailedAttempts.Should().Be(1); } @@ -91,18 +91,18 @@ await orchestrator.LoginAsync(flow, { Identifier = "user", Secret = "wrong", - }); + }, ct: TestContext.Current.CancellationToken); await orchestrator.LoginAsync(flow, new LoginRequest { Identifier = "user", Secret = "user", // valid password - }); + }, ct: TestContext.Current.CancellationToken); var factory = runtime.Services.GetRequiredService(); var store = factory.Create(TenantKeys.Single); - var state = await store.GetAsync(TestUsers.User, AuthenticationSecurityScope.Factor, CredentialType.Password); + var state = await store.GetAsync(TestUsers.User, AuthenticationSecurityScope.Factor, CredentialType.Password, ct: TestContext.Current.CancellationToken); state?.FailedAttempts.Should().Be(0); } @@ -118,7 +118,7 @@ public async Task Invalid_password_should_fail_login() { Identifier = "user", Secret = "wrong", - }); + }, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); } @@ -135,7 +135,7 @@ public async Task Non_existent_user_should_fail_login_gracefully() { Identifier = "ghost", Secret = "whatever", - }); + }, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); } @@ -156,11 +156,11 @@ await orchestrator.LoginAsync(flow, { Identifier = "user", Secret = "wrong", - }); + }, ct: TestContext.Current.CancellationToken); var factory = runtime.Services.GetRequiredService(); var store = factory.Create(TenantKeys.Single); - var state = await store.GetAsync(TestUsers.User, AuthenticationSecurityScope.Factor, CredentialType.Password); + var state = await store.GetAsync(TestUsers.User, AuthenticationSecurityScope.Factor, CredentialType.Password, ct: TestContext.Current.CancellationToken); state!.IsLocked(DateTimeOffset.UtcNow).Should().BeTrue(); } @@ -181,14 +181,14 @@ await orchestrator.LoginAsync(flow, { Identifier = "user", Secret = "wrong", - }); + }, ct: TestContext.Current.CancellationToken); var result = await orchestrator.LoginAsync(flow, new LoginRequest { Identifier = "user", Secret = "user", - }); + }, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); } @@ -209,19 +209,19 @@ await orchestrator.LoginAsync(flow, { Identifier = "user", Secret = "wrong", - }); + }, ct: TestContext.Current.CancellationToken); var factory = runtime.Services.GetRequiredService(); var store = factory.Create(TenantKeys.Single); - var state1 = await store.GetAsync(TestUsers.User, AuthenticationSecurityScope.Factor, CredentialType.Password); + var state1 = await store.GetAsync(TestUsers.User, AuthenticationSecurityScope.Factor, CredentialType.Password, ct: TestContext.Current.CancellationToken); await orchestrator.LoginAsync(flow, new LoginRequest { Identifier = "user", - }); + }, ct: TestContext.Current.CancellationToken); - var state2 = await store.GetAsync(TestUsers.User, AuthenticationSecurityScope.Factor, CredentialType.Password); + var state2 = await store.GetAsync(TestUsers.User, AuthenticationSecurityScope.Factor, CredentialType.Password, ct: TestContext.Current.CancellationToken); state2?.FailedAttempts.Should().Be(state1!.FailedAttempts); } @@ -243,12 +243,12 @@ await orchestrator.LoginAsync(flow, { Identifier = "user", Secret = "wrong", - }); + }, ct: TestContext.Current.CancellationToken); } var factory = runtime.Services.GetRequiredService(); var store = factory.Create(TenantKeys.Single); - var state = await store.GetAsync(TestUsers.User, AuthenticationSecurityScope.Factor, CredentialType.Password); + var state = await store.GetAsync(TestUsers.User, AuthenticationSecurityScope.Factor, CredentialType.Password, ct: TestContext.Current.CancellationToken); state?.IsLocked(DateTimeOffset.UtcNow).Should().BeFalse(); state?.FailedAttempts.Should().Be(5); @@ -271,11 +271,11 @@ await orchestrator.LoginAsync(flow, { Identifier = "user", Secret = "wrong", - }); + }, ct: TestContext.Current.CancellationToken); var factory = runtime.Services.GetRequiredService(); var store = factory.Create(TenantKeys.Single); - var state1 = await store.GetAsync(TestUsers.User, AuthenticationSecurityScope.Factor, CredentialType.Password); + var state1 = await store.GetAsync(TestUsers.User, AuthenticationSecurityScope.Factor, CredentialType.Password, ct: TestContext.Current.CancellationToken); var lockedUntil = state1!.LockedUntil; @@ -284,9 +284,9 @@ await orchestrator.LoginAsync(flow, { Identifier = "user", Secret = "wrong", - }); + }, ct: TestContext.Current.CancellationToken); - var state2 = await store.GetAsync(TestUsers.User, AuthenticationSecurityScope.Factor, CredentialType.Password); + var state2 = await store.GetAsync(TestUsers.User, AuthenticationSecurityScope.Factor, CredentialType.Password, ct: TestContext.Current.CancellationToken); state2?.LockedUntil.Should().Be(lockedUntil); } @@ -311,7 +311,7 @@ public async Task Login_success_should_trigger_UserLoggedIn_event() { Identifier = "user", Secret = "user", - }); + }, ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); captured!.UserKey.Should().Be(TestUsers.User); @@ -338,7 +338,7 @@ public async Task Login_success_should_trigger_OnAnyEvent() { Identifier = "user", Secret = "user", - }); + }, ct: TestContext.Current.CancellationToken); count.Should().BeGreaterThan(0); } @@ -358,7 +358,7 @@ public async Task Event_handler_exception_should_not_break_login_flow() { Identifier = "user", Secret = "user", - }); + }, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); } @@ -395,13 +395,13 @@ public async Task Login_With_Execution_Context_Should_Create_Session_For_Overrid result.SessionId.Should().NotBeNull(); var sessionStore = sessionStoreFactory.Create(TenantKeys.Single); - var session = await sessionStore.GetSessionAsync(result.SessionId!.Value); + var session = await sessionStore.GetSessionAsync(result.SessionId!.Value, ct: TestContext.Current.CancellationToken); session.Should().NotBeNull(); session!.Device.DeviceId.Should().Be(overriddenDevice.DeviceId); var chainStore = sessionStoreFactory.Create(TenantKeys.Single); - var chain = await chainStore.GetChainByDeviceAsync(TestUsers.User, (DeviceId)overriddenDevice.DeviceId!); + var chain = await chainStore.GetChainByDeviceAsync(TestUsers.User, (DeviceId)overriddenDevice.DeviceId!, ct: TestContext.Current.CancellationToken); chain.Should().NotBeNull(); chain!.Device.DeviceId.Should().Be(overriddenDevice.DeviceId); @@ -429,7 +429,7 @@ public async Task Internal_Login_Should_Respect_LoginExecutionOptions() Identifier = "user", Secret = "user" }, - options); + options, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); } @@ -438,10 +438,10 @@ public async Task Internal_Login_Should_Respect_LoginExecutionOptions() public async Task Concurrent_First_Logins_FromDifferentDevices_ShouldCreateSingleRootAndDifferentChains() { var runtime = new TestAuthRuntime(); - var user = await runtime.CreateLoginUserAsync(); + var user = await runtime.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var storeFactory = runtime.Services.GetRequiredService(); var store = storeFactory.Create(TenantKeys.Single); - var rootBefore = await store.GetActiveRootByUserAsync(user.UserKey); + var rootBefore = await store.GetActiveRootByUserAsync(user.UserKey, ct: TestContext.Current.CancellationToken); rootBefore.Should().BeNull(); @@ -472,8 +472,8 @@ public async Task Concurrent_First_Logins_FromDifferentDevices_ShouldCreateSingl Secret = user.Secret }; - var taskA = runtime.LoginAsync(flowA, executionA, requestA); - var taskB = runtime.LoginAsync(flowB, executionB, requestB); + var taskA = runtime.LoginAsync(flowA, executionA, requestA, ct: TestContext.Current.CancellationToken); + var taskB = runtime.LoginAsync(flowB, executionB, requestB, ct: TestContext.Current.CancellationToken); var results = await Task.WhenAll(taskA, taskB); @@ -486,11 +486,11 @@ public async Task Concurrent_First_Logins_FromDifferentDevices_ShouldCreateSingl var sessionA = await store.GetSessionAsync( - results[0].SessionId!.Value); + results[0].SessionId!.Value, ct: TestContext.Current.CancellationToken); var sessionB = await store.GetSessionAsync( - results[1].SessionId!.Value); + results[1].SessionId!.Value, ct: TestContext.Current.CancellationToken); sessionA.Should().NotBeNull(); sessionB.Should().NotBeNull(); @@ -500,11 +500,11 @@ await store.GetSessionAsync( var chainA = await store.GetChainAsync( - sessionA.ChainId); + sessionA.ChainId, ct: TestContext.Current.CancellationToken); var chainB = await store.GetChainAsync( - sessionB.ChainId); + sessionB.ChainId, ct: TestContext.Current.CancellationToken); chainA.Should().NotBeNull(); chainB.Should().NotBeNull(); @@ -514,7 +514,7 @@ await store.GetChainAsync( var activeRoot = await store.GetActiveRootByUserAsync( - user.UserKey); + user.UserKey, ct: TestContext.Current.CancellationToken); activeRoot.Should().NotBeNull(); @@ -534,7 +534,7 @@ public async Task Concurrent_First_Logins_FromSameDevice_ShouldCreateSingleRootA // Fresh user: no Root, Chain or Session exists. // var user = - await runtime.CreateLoginUserAsync(); + await runtime.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); var storeFactory = runtime.Services.GetRequiredService(); @@ -544,7 +544,7 @@ public async Task Concurrent_First_Logins_FromSameDevice_ShouldCreateSingleRootA var rootBefore = await store.GetActiveRootByUserAsync( - user.UserKey); + user.UserKey, ct: TestContext.Current.CancellationToken); rootBefore.Should().BeNull(); @@ -595,13 +595,13 @@ await store.GetActiveRootByUserAsync( runtime.LoginAsync( flowA, executionA, - requestA); + requestA, ct: TestContext.Current.CancellationToken); var taskB = runtime.LoginAsync( flowB, executionB, - requestB); + requestB, ct: TestContext.Current.CancellationToken); var results = await Task.WhenAll( @@ -616,11 +616,11 @@ await Task.WhenAll( var sessionA = await store.GetSessionAsync( - results[0].SessionId!.Value); + results[0].SessionId!.Value, ct: TestContext.Current.CancellationToken); var sessionB = await store.GetSessionAsync( - results[1].SessionId!.Value); + results[1].SessionId!.Value, ct: TestContext.Current.CancellationToken); sessionA.Should().NotBeNull(); sessionB.Should().NotBeNull(); @@ -633,7 +633,7 @@ await store.GetSessionAsync( var chain = await store.GetChainAsync( - sessionA.ChainId); + sessionA.ChainId, ct: TestContext.Current.CancellationToken); chain.Should().NotBeNull(); @@ -642,7 +642,7 @@ await store.GetChainAsync( // var activeRoot = await store.GetActiveRootByUserAsync( - user.UserKey); + user.UserKey, ct: TestContext.Current.CancellationToken); activeRoot.Should().NotBeNull(); @@ -700,7 +700,7 @@ public async Task Unsupported_credential_type_should_not_affect_password_lockout var state = await store.GetAsync( TestUsers.User, AuthenticationSecurityScope.Factor, - CredentialType.Password); + CredentialType.Password, ct: TestContext.Current.CancellationToken); (state?.FailedAttempts ?? 0).Should().Be(0); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/PkceServiceTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/PkceServiceTests.cs index 845460e6..c8921eb7 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/PkceServiceTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/PkceServiceTests.cs @@ -103,7 +103,7 @@ public async Task AuthorizeAsync_ShouldStorePkceAuthorizationArtifact() .Returns(Task.CompletedTask); var result = - await fixture.Sut.AuthorizeAsync(command); + await fixture.Sut.AuthorizeAsync(command, ct: TestContext.Current.CancellationToken); capturedKey.Should().NotBeNull(); capturedArtifact.Should().NotBeNull(); @@ -182,7 +182,7 @@ public async Task CompleteAsync_WhenAuthorizationArtifactDoesNotExist_ShouldRetu var result = await fixture.Sut.CompleteAsync( auth, - request); + request, ct: TestContext.Current.CancellationToken); result.InvalidPkce.Should().BeTrue(); @@ -224,7 +224,7 @@ public async Task CompleteAsync_ShouldConsumeAuthorizationCodeUsingRequestCode() await fixture.Sut.CompleteAsync( AuthFlowTestFactory.New(), - request); + request, ct: TestContext.Current.CancellationToken); fixture.Store.Verify( x => x.ConsumeAsync( @@ -257,7 +257,7 @@ public async Task CompleteAsync_WhenPkceValidationFails_ShouldNotAttemptLogin() var result = await fixture.Sut.CompleteAsync( AuthFlowTestFactory.New(), - CreateCompleteRequest()); + CreateCompleteRequest(), ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); result.FailureReason @@ -309,7 +309,7 @@ public async Task CompleteAsync_WhenValidationSucceeds_ShouldPassCredentialsToLo await fixture.Sut.CompleteAsync( AuthFlowTestFactory.New(), - request); + request, ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); @@ -357,7 +357,7 @@ public async Task CompleteAsync_WhenValidationSucceeds_ShouldUseArtifactExecutio await fixture.Sut.CompleteAsync( AuthFlowTestFactory.New(), - CreateCompleteRequest()); + CreateCompleteRequest(), ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); @@ -423,7 +423,7 @@ public async Task RefreshAsync_WhenOldAuthorizationCodeExists_ShouldConsumeIt() var hub = CreateHubArtifact( authorizationCode: "old-code"); - await fixture.Sut.RefreshAsync(hub); + await fixture.Sut.RefreshAsync(hub, ct: TestContext.Current.CancellationToken); fixture.Store.Verify( x => x.ConsumeAsync( @@ -446,7 +446,7 @@ public async Task RefreshAsync_WhenOldAuthorizationCodeDoesNotExist_ShouldNotCon var hub = CreateHubArtifact( authorizationCode: oldCode); - await fixture.Sut.RefreshAsync(hub); + await fixture.Sut.RefreshAsync(hub, ct: TestContext.Current.CancellationToken); fixture.Store.Verify( x => x.ConsumeAsync( @@ -483,7 +483,7 @@ public async Task RefreshAsync_ShouldCreateNewPkceArtifactWithHubContext() .Returns(Task.CompletedTask); var result = - await fixture.Sut.RefreshAsync(hub); + await fixture.Sut.RefreshAsync(hub, ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); @@ -536,7 +536,7 @@ public async Task RefreshAsync_ReturnedVerifier_ShouldMatchStoredS256Challenge() .Returns(Task.CompletedTask); var result = - await fixture.Sut.RefreshAsync(hub); + await fixture.Sut.RefreshAsync(hub, ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); @@ -600,7 +600,7 @@ public async Task CompleteAsync_WhenLoginFails_ShouldMapLoginFailure() var result = await fixture.Sut.CompleteAsync( AuthFlowTestFactory.New(), - CreateCompleteRequest()); + CreateCompleteRequest(), ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); @@ -635,7 +635,7 @@ public async Task CompleteAsync_WhenLoginSucceeds_ShouldReturnLoginResult() var result = await fixture.Sut.CompleteAsync( AuthFlowTestFactory.New(), - CreateCompleteRequest()); + CreateCompleteRequest(), ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); result.FailureReason.Should().BeNull(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/PkceTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/PkceTests.cs index c5119182..4be8a36e 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/PkceTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/PkceTests.cs @@ -61,11 +61,11 @@ public async Task Refresh_Should_Generate_New_AuthorizationCode() var (artifact, _) = TestPkceFactory.Create(); - await store.StoreAsync(artifact.AuthorizationCode, artifact); + await store.StoreAsync(artifact.AuthorizationCode, artifact, cancellationToken: TestContext.Current.CancellationToken); var hub = TestHubFactory.Create(artifact); - var refreshed = await pkceService.RefreshAsync(hub); + var refreshed = await pkceService.RefreshAsync(hub, ct: TestContext.Current.CancellationToken); refreshed.AuthorizationCode.Should().NotBe(artifact.AuthorizationCode.Value); } @@ -82,13 +82,13 @@ public async Task Refresh_Should_Store_New_PkceArtifact() var (artifact, _) = TestPkceFactory.Create(); - await store.StoreAsync(artifact.AuthorizationCode, artifact); + await store.StoreAsync(artifact.AuthorizationCode, artifact, cancellationToken: TestContext.Current.CancellationToken); var hub = TestHubFactory.Create(artifact); - var refreshed = await pkceService.RefreshAsync(hub); + var refreshed = await pkceService.RefreshAsync(hub, ct: TestContext.Current.CancellationToken); - var stored = await store.GetAsync(new AuthArtifactKey(refreshed.AuthorizationCode)); + var stored = await store.GetAsync(new AuthArtifactKey(refreshed.AuthorizationCode), cancellationToken: TestContext.Current.CancellationToken); stored.Should().NotBeNull(); } @@ -102,10 +102,10 @@ public async Task Refresh_Should_Invalidate_Old_Code() var pkceService = scope.ServiceProvider.GetRequiredService(); var (artifact, _) = TestPkceFactory.Create(); - await store.StoreAsync(artifact.AuthorizationCode, artifact); + await store.StoreAsync(artifact.AuthorizationCode, artifact, cancellationToken: TestContext.Current.CancellationToken); var hub = TestHubFactory.Create(artifact); - var refreshed = await pkceService.RefreshAsync(hub); - var old = await store.GetAsync(artifact.AuthorizationCode); + var refreshed = await pkceService.RefreshAsync(hub, ct: TestContext.Current.CancellationToken); + var old = await store.GetAsync(artifact.AuthorizationCode, cancellationToken: TestContext.Current.CancellationToken); old.Should().BeNull(); } @@ -119,10 +119,10 @@ public async Task Refresh_Should_Preserve_Context() var (artifact, _) = TestPkceFactory.Create(); var hub = TestHubFactory.Create(artifact); - var refreshed = await pkceService.RefreshAsync(hub); + var refreshed = await pkceService.RefreshAsync(hub, ct: TestContext.Current.CancellationToken); var store = scope.ServiceProvider.GetRequiredService(); - var newArtifact = await store.GetAsync(new AuthArtifactKey(refreshed.AuthorizationCode)) as PkceAuthorizationArtifact; + var newArtifact = await store.GetAsync(new AuthArtifactKey(refreshed.AuthorizationCode), cancellationToken: TestContext.Current.CancellationToken) as PkceAuthorizationArtifact; newArtifact!.Context.Device.Should().BeEquivalentTo(artifact.Context.Device); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/RateLimitingPartitionTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/RateLimitingPartitionTests.cs index 5a6cea5b..971422f2 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/RateLimitingPartitionTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/RateLimitingPartitionTests.cs @@ -29,7 +29,7 @@ public async Task RejectionForwardsNativeRetryMetadata() context.Response.Body = new MemoryStream(); var policy = new UAuthRateLimitPolicy(UAuthEndpointClassification.Get(UAuthEndpoint.Login), new(), "login"); await policy.OnRejected!(new Microsoft.AspNetCore.RateLimiting.OnRejectedContext - { HttpContext = context, Lease = rejected }, default); + { HttpContext = context, Lease = rejected }, TestContext.Current.CancellationToken); Assert.Equal(429, context.Response.StatusCode); Assert.Equal("no-store", context.Response.Headers.CacheControl.ToString()); Assert.True(int.Parse(context.Response.Headers.RetryAfter.ToString()) > 0); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/RefreshFlowServiceTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/RefreshFlowServiceTests.cs index 76be15d8..19bf170d 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/RefreshFlowServiceTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/RefreshFlowServiceTests.cs @@ -29,7 +29,7 @@ public async Task RefreshAsync_PureOpaque_WhenSessionIdMissing_ReturnsReauthWith var result = await sut.RefreshAsync( CreateFlow(UAuthMode.PureOpaque), - CreateRequest(sessionId: null)); + CreateRequest(sessionId: null), ct: TestContext.Current.CancellationToken); AssertReauth(result); validator.VerifyNoOtherCalls(); @@ -54,7 +54,7 @@ public async Task RefreshAsync_PureOpaque_WhenSessionInvalid_ReturnsReauthWithou var result = await sut.RefreshAsync( CreateFlow(UAuthMode.PureOpaque), - CreateRequest(SessionId)); + CreateRequest(SessionId), ct: TestContext.Current.CancellationToken); AssertReauth(result); touch.VerifyNoOtherCalls(); @@ -88,7 +88,7 @@ public async Task RefreshAsync_PureOpaque_WhenSessionRefreshSucceeds_ReturnsExpe var result = await sut.RefreshAsync( CreateFlow(UAuthMode.PureOpaque), - CreateRequest(SessionId)); + CreateRequest(SessionId), ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); result.Outcome.Should().Be(expectedOutcome); @@ -117,7 +117,7 @@ public async Task RefreshAsync_PureOpaque_WhenTouchFails_ReturnsReauth() var result = await sut.RefreshAsync( CreateFlow(UAuthMode.PureOpaque), - CreateRequest(SessionId)); + CreateRequest(SessionId), ct: TestContext.Current.CancellationToken); AssertReauth(result); rotation.VerifyNoOtherCalls(); @@ -130,7 +130,7 @@ public async Task RefreshAsync_PureJwt_WhenRefreshTokenMissing_ReturnsReauthWith var result = await sut.RefreshAsync( CreateFlow(UAuthMode.PureJwt), - CreateRequest(refreshToken: " ")); + CreateRequest(refreshToken: " "), ct: TestContext.Current.CancellationToken); AssertReauth(result); validator.VerifyNoOtherCalls(); @@ -157,7 +157,7 @@ public async Task RefreshAsync_PureJwt_WhenRotationSucceeds_ReturnsRotatedTokens var result = await sut.RefreshAsync( CreateFlow(UAuthMode.PureJwt), - CreateRequest(refreshToken: "refresh-token")); + CreateRequest(refreshToken: "refresh-token"), ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); result.Outcome.Should().Be(RefreshOutcome.Rotated); @@ -185,7 +185,7 @@ public async Task RefreshAsync_PureJwt_WhenRotationFails_ReturnsReauth() var result = await sut.RefreshAsync( CreateFlow(UAuthMode.PureJwt), - CreateRequest(refreshToken: "refresh-token")); + CreateRequest(refreshToken: "refresh-token"), ct: TestContext.Current.CancellationToken); AssertReauth(result); validator.VerifyNoOtherCalls(); @@ -204,7 +204,7 @@ public async Task RefreshAsync_Hybrid_WhenRequiredCredentialMissing_ReturnsReaut var result = await sut.RefreshAsync( CreateFlow(UAuthMode.Hybrid), - CreateRequest(sessionMarker is null ? null : SessionId, refreshToken)); + CreateRequest(sessionMarker is null ? null : SessionId, refreshToken), ct: TestContext.Current.CancellationToken); AssertReauth(result); validator.VerifyNoOtherCalls(); @@ -223,7 +223,7 @@ public async Task RefreshAsync_Hybrid_WhenSessionInvalid_ReturnsReauthWithoutRot var result = await sut.RefreshAsync( CreateFlow(UAuthMode.Hybrid), - CreateRequest(SessionId, "refresh-token")); + CreateRequest(SessionId, "refresh-token"), ct: TestContext.Current.CancellationToken); AssertReauth(result); rotation.VerifyNoOtherCalls(); @@ -266,7 +266,7 @@ public async Task RefreshAsync_Hybrid_WhenRotationFails_ReturnsReauthAfterSucces var result = await sut.RefreshAsync( CreateFlow(UAuthMode.Hybrid), - CreateRequest(SessionId, "refresh-token")); + CreateRequest(SessionId, "refresh-token"), ct: TestContext.Current.CancellationToken); AssertReauth(result); @@ -314,7 +314,7 @@ public async Task RefreshAsync_Hybrid_WhenAllStepsSucceed_ReturnsRotatedResultAn var result = await sut.RefreshAsync( CreateFlow(UAuthMode.Hybrid), - CreateRequest(SessionId, "refresh-token")); + CreateRequest(SessionId, "refresh-token"), ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); result.Outcome.Should().Be(RefreshOutcome.Rotated); @@ -346,7 +346,7 @@ public async Task RefreshAsync_Hybrid_WhenTouchFails_DoesNotRotateRefreshToken() var result = await sut.RefreshAsync( CreateFlow(UAuthMode.Hybrid), - CreateRequest(SessionId, "refresh-token")); + CreateRequest(SessionId, "refresh-token"), ct: TestContext.Current.CancellationToken); AssertReauth(result); @@ -376,7 +376,7 @@ public async Task RefreshAsync_SemiHybrid_WhenSessionInvalid_ReturnsReauthWithou var result = await sut.RefreshAsync( CreateFlow(UAuthMode.SemiHybrid), - CreateRequest(SessionId, "refresh-token")); + CreateRequest(SessionId, "refresh-token"), ct: TestContext.Current.CancellationToken); AssertReauth(result); rotation.VerifyNoOtherCalls(); @@ -405,7 +405,7 @@ public async Task RefreshAsync_SemiHybrid_WhenRotationSucceeds_ReturnsRotatedWit var result = await sut.RefreshAsync( CreateFlow(UAuthMode.SemiHybrid), - CreateRequest(SessionId, "refresh-token")); + CreateRequest(SessionId, "refresh-token"), ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); result.Outcome.Should().Be(RefreshOutcome.Rotated); @@ -437,7 +437,7 @@ public async Task RefreshAsync_SemiHybrid_WhenRotationFails_ReturnsReauthWithout var result = await sut.RefreshAsync( CreateFlow(UAuthMode.SemiHybrid), - CreateRequest(SessionId, "refresh-token")); + CreateRequest(SessionId, "refresh-token"), ct: TestContext.Current.CancellationToken); AssertReauth(result); touch.VerifyNoOtherCalls(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/RefreshTokenRotationServiceTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/RefreshTokenRotationServiceTests.cs index 8d26537d..ffd2d6c3 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/RefreshTokenRotationServiceTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/RefreshTokenRotationServiceTests.cs @@ -35,7 +35,7 @@ public async Task RotateAsync_WhenValidationIsInvalid_ReturnsFailedWithoutCreati var sut = new RefreshTokenRotationService(validator.Object, storeFactory.Object, issuer.Object, claimsProvider.Object); - var result = await sut.RotateAsync(CreateFlow(), CreateContext()); + var result = await sut.RotateAsync(CreateFlow(), CreateContext(), ct: TestContext.Current.CancellationToken); result.Result.IsSuccess.Should().BeFalse(); result.Result.ReauthRequired.Should().BeTrue(); @@ -73,7 +73,7 @@ public async Task RotateAsync_WhenTokenIsNotFound_ReturnsFailedWithoutCreatingSt var result = await sut.RotateAsync( CreateFlow(), - CreateContext()); + CreateContext(), ct: TestContext.Current.CancellationToken); result.Result.IsSuccess.Should().BeFalse(); result.Result.ReauthRequired.Should().BeTrue(); @@ -131,7 +131,7 @@ public async Task RotateAsync_WhenTokenIsExpired_ReturnsFailedWithoutMutatingSto var result = await sut.RotateAsync( CreateFlow(tenant, multiTenant: true), - CreateContext(token.SessionId)); + CreateContext(token.SessionId), ct: TestContext.Current.CancellationToken); result.Result.IsSuccess.Should().BeFalse(); @@ -225,7 +225,7 @@ await sut.RotateAsync( CreateFlow( tenant, multiTenant: true), - CreateContext(sessionId)); + CreateContext(sessionId), ct: TestContext.Current.CancellationToken); result.Result.IsSuccess.Should() .BeFalse(); @@ -288,7 +288,7 @@ public async Task RotateAsync_WhenRefreshTokenCannotBeIssued_ReturnsFailedWithou var result = await sut.RotateAsync( CreateFlow(tenant, multiTenant: true), - CreateContext(validation.SessionId)); + CreateContext(validation.SessionId), ct: TestContext.Current.CancellationToken); result.Result.IsSuccess.Should().BeFalse(); result.Result.ReauthRequired.Should().BeTrue(); @@ -345,7 +345,7 @@ public async Task RotateAsync_WhenValid_AtomicallyRotatesRefreshToken() var sut = new RefreshTokenRotationService(validator.Object, storeFactory.Object, issuer.Object, claimsProvider.Object); - var result = await sut.RotateAsync(CreateFlow(tenant, multiTenant: true), CreateContext(validation.SessionId)); + var result = await sut.RotateAsync(CreateFlow(tenant, multiTenant: true), CreateContext(validation.SessionId), ct: TestContext.Current.CancellationToken); result.Result.IsSuccess.Should().BeTrue(); result.Result.AccessToken.Should().BeSameAs(accessToken); @@ -420,7 +420,7 @@ public async Task RotateAsync_WhenMultiTenantEnabled_UsesValidatedTenantForToken CreateFlow( tenant, multiTenant: true), - CreateContext(validation.SessionId)); + CreateContext(validation.SessionId), ct: TestContext.Current.CancellationToken); result.Result.IsSuccess.Should().BeTrue(); @@ -462,7 +462,7 @@ public async Task RotateAsync_WhenMultiTenantDisabled_UsesSingleTenantForTokenIs CreateFlow( TenantKey.Single, multiTenant: false), - CreateContext(validation.SessionId)); + CreateContext(validation.SessionId), ct: TestContext.Current.CancellationToken); result.Result.IsSuccess.Should().BeTrue(); @@ -519,7 +519,7 @@ public async Task RotateAsync_WhenAtomicConsumeLosesRace_ReturnsFailedWithoutSto var result = await sut.RotateAsync( CreateFlow(tenant, multiTenant: true), - CreateContext(validation.SessionId)); + CreateContext(validation.SessionId), ct: TestContext.Current.CancellationToken); result.Result.IsSuccess.Should().BeFalse(); result.Result.ReauthRequired.Should().BeTrue(); @@ -639,7 +639,7 @@ public async Task RotateAsync_WhenIssueRefreshDisabled_ShouldFailWithoutValidati var result = await sut.RotateAsync( flow, - CreateContext()); + CreateContext(), ct: TestContext.Current.CancellationToken); result.Result.IsSuccess.Should().BeFalse(); result.Result.ReauthRequired.Should().BeTrue(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/ResourceAccessContextBuilderTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/ResourceAccessContextBuilderTests.cs index 3938678b..5ec03c83 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/ResourceAccessContextBuilderTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/ResourceAccessContextBuilderTests.cs @@ -27,7 +27,7 @@ public async Task Create_WithAuthenticatedUser_ShouldSetActor() var http = CreateHttpContext(authenticated: true, userKey); - var result = await ResourceAccessContextBuilder.CreateAsync(http, "users.read"); + var result = await ResourceAccessContextBuilder.CreateAsync(http, "users.read", ct: TestContext.Current.CancellationToken); result.IsAuthenticated.Should().BeTrue(); result.ActorUserKey.Should().Be(userKey); @@ -40,7 +40,7 @@ public async Task Create_WithAnonymousUser_ShouldNotSetActorUserKey() { var http = CreateHttpContext(authenticated: false); - var result = await ResourceAccessContextBuilder.CreateAsync(http, "users.read"); + var result = await ResourceAccessContextBuilder.CreateAsync(http, "users.read", ct: TestContext.Current.CancellationToken); result.IsAuthenticated.Should().BeFalse(); result.ActorUserKey.Should().BeNull(); @@ -56,7 +56,7 @@ public async Task Create_ShouldDeriveResourceFromAction(string action, string ex { var http = CreateHttpContext(authenticated: false); - var result = await ResourceAccessContextBuilder.CreateAsync(http, action); + var result = await ResourceAccessContextBuilder.CreateAsync(http, action, ct: TestContext.Current.CancellationToken); result.Resource.Should().Be(expectedResource); result.Action.Should().Be(action); @@ -67,7 +67,7 @@ public async Task Create_ShouldNotTreatCurrentUserAsSystemActor() { var http = CreateHttpContext(authenticated: true, CreateUserKey()); - var result = await ResourceAccessContextBuilder.CreateAsync(http, "users.read"); + var result = await ResourceAccessContextBuilder.CreateAsync(http, "users.read", ct: TestContext.Current.CancellationToken); result.IsSystemActor.Should().BeFalse(); } @@ -77,7 +77,7 @@ public async Task Create_ShouldNotSetTargetUser() { var http = CreateHttpContext(authenticated: true, CreateUserKey()); - var result = await ResourceAccessContextBuilder.CreateAsync(http, "users.read"); + var result = await ResourceAccessContextBuilder.CreateAsync(http, "users.read", ct: TestContext.Current.CancellationToken); result.TargetUserKey.Should().BeNull(); } @@ -89,7 +89,7 @@ public async Task Create_ShouldPropagateTenantToActorAndResource() var http = CreateHttpContext(authenticated: true, userKey: CreateUserKey(), tenant: tenant); - var result = await ResourceAccessContextBuilder.CreateAsync(http, "users.read"); + var result = await ResourceAccessContextBuilder.CreateAsync(http, "users.read", ct: TestContext.Current.CancellationToken); result.ActorTenant.Should().Be(tenant); result.ResourceTenant.Should().Be(tenant); @@ -179,7 +179,7 @@ public async Task Create_WithResourceRuntime_ShouldResolveTrustedActorRolesFromC // Act var context = await ResourceAccessContextBuilder.CreateAsync( http, - "users.read"); + "users.read", ct: TestContext.Current.CancellationToken); // Assert context.IsAuthenticated.Should().BeTrue(); @@ -255,7 +255,7 @@ public async Task Create_WithResourceRuntime_ShouldIgnoreRolesFromOtherIdentitie // Act var context = await ResourceAccessContextBuilder.CreateAsync( http, - "users.read"); + "users.read", ct: TestContext.Current.CancellationToken); // Assert context.ActorRoles.Should().ContainSingle(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/SessionApplicationServiceTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/SessionApplicationServiceTests.cs index 48c01bd4..80fed14b 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/SessionApplicationServiceTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/SessionApplicationServiceTests.cs @@ -31,13 +31,13 @@ public async Task GetUserChainsAsync_ReturnsRequestedPage() var root = await CreateRootAsync(store, user); await store.CreateChainAsync( - CreateChain(root, Now.AddMinutes(-30))); + CreateChain(root, Now.AddMinutes(-30)), ct: TestContext.Current.CancellationToken); await store.CreateChainAsync( - CreateChain(root, Now.AddMinutes(-20))); + CreateChain(root, Now.AddMinutes(-20)), ct: TestContext.Current.CancellationToken); await store.CreateChainAsync( - CreateChain(root, Now.AddMinutes(-10))); + CreateChain(root, Now.AddMinutes(-10)), ct: TestContext.Current.CancellationToken); var result = await sut.GetUserChainsAsync( Context(user), @@ -46,7 +46,7 @@ await store.CreateChainAsync( { PageNumber = 2, PageSize = 2 - }); + }, ct: TestContext.Current.CancellationToken); result.TotalCount.Should().Be(3); result.PageNumber.Should().Be(2); @@ -70,8 +70,8 @@ public async Task GetUserChainsAsync_MarksActorChainAsCurrentDevice() root, Now.AddMinutes(-5)); - await store.CreateChainAsync(actorChain); - await store.CreateChainAsync(otherChain); + await store.CreateChainAsync(actorChain, ct: TestContext.Current.CancellationToken); + await store.CreateChainAsync(otherChain, ct: TestContext.Current.CancellationToken); var result = await sut.GetUserChainsAsync( Context(user, actorChain.ChainId), @@ -80,7 +80,7 @@ public async Task GetUserChainsAsync_MarksActorChainAsCurrentDevice() { PageNumber = 1, PageSize = 10 - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Should().HaveCount(2); @@ -111,9 +111,9 @@ public async Task GetUserChainsAsync_SortByChainIdAscending_SortsByChainId() var second = CreateChain(root, Now.AddMinutes(-20)); var third = CreateChain(root, Now.AddMinutes(-10)); - await store.CreateChainAsync(first); - await store.CreateChainAsync(second); - await store.CreateChainAsync(third); + await store.CreateChainAsync(first, ct: TestContext.Current.CancellationToken); + await store.CreateChainAsync(second, ct: TestContext.Current.CancellationToken); + await store.CreateChainAsync(third, ct: TestContext.Current.CancellationToken); var result = await sut.GetUserChainsAsync( Context(user), @@ -124,7 +124,7 @@ public async Task GetUserChainsAsync_SortByChainIdAscending_SortsByChainId() PageSize = 10, SortBy = nameof(SessionChainSummary.ChainId), Descending = false - }); + }, ct: TestContext.Current.CancellationToken); result.Items .Select(x => x.ChainId.Value) @@ -141,13 +141,13 @@ public async Task GetUserChainsAsync_SortByChainIdDescending_SortsByChainId() var root = await CreateRootAsync(store, user); await store.CreateChainAsync( - CreateChain(root, Now.AddMinutes(-30))); + CreateChain(root, Now.AddMinutes(-30)), ct: TestContext.Current.CancellationToken); await store.CreateChainAsync( - CreateChain(root, Now.AddMinutes(-20))); + CreateChain(root, Now.AddMinutes(-20)), ct: TestContext.Current.CancellationToken); await store.CreateChainAsync( - CreateChain(root, Now.AddMinutes(-10))); + CreateChain(root, Now.AddMinutes(-10)), ct: TestContext.Current.CancellationToken); var result = await sut.GetUserChainsAsync( Context(user), @@ -158,7 +158,7 @@ await store.CreateChainAsync( PageSize = 10, SortBy = nameof(SessionChainSummary.ChainId), Descending = true - }); + }, ct: TestContext.Current.CancellationToken); result.Items .Select(x => x.ChainId.Value) @@ -176,13 +176,13 @@ public async Task GetUserChainsAsync_SortByCreatedAtAscending_SortsByCreatedAt() // Deliberately insert in non-chronological order. await store.CreateChainAsync( - CreateChain(root, Now.AddMinutes(-5))); + CreateChain(root, Now.AddMinutes(-5)), ct: TestContext.Current.CancellationToken); await store.CreateChainAsync( - CreateChain(root, Now.AddMinutes(-30))); + CreateChain(root, Now.AddMinutes(-30)), ct: TestContext.Current.CancellationToken); await store.CreateChainAsync( - CreateChain(root, Now.AddMinutes(-15))); + CreateChain(root, Now.AddMinutes(-15)), ct: TestContext.Current.CancellationToken); var result = await sut.GetUserChainsAsync( Context(user), @@ -193,7 +193,7 @@ await store.CreateChainAsync( PageSize = 10, SortBy = nameof(SessionChainSummary.CreatedAt), Descending = false - }); + }, ct: TestContext.Current.CancellationToken); result.Items .Select(x => x.CreatedAt) @@ -210,13 +210,13 @@ public async Task GetUserChainsAsync_SortByCreatedAtDescending_SortsByCreatedAt( var root = await CreateRootAsync(store, user); await store.CreateChainAsync( - CreateChain(root, Now.AddMinutes(-5))); + CreateChain(root, Now.AddMinutes(-5)), ct: TestContext.Current.CancellationToken); await store.CreateChainAsync( - CreateChain(root, Now.AddMinutes(-30))); + CreateChain(root, Now.AddMinutes(-30)), ct: TestContext.Current.CancellationToken); await store.CreateChainAsync( - CreateChain(root, Now.AddMinutes(-15))); + CreateChain(root, Now.AddMinutes(-15)), ct: TestContext.Current.CancellationToken); var result = await sut.GetUserChainsAsync( Context(user), @@ -227,7 +227,7 @@ await store.CreateChainAsync( PageSize = 10, SortBy = nameof(SessionChainSummary.CreatedAt), Descending = true - }); + }, ct: TestContext.Current.CancellationToken); result.Items .Select(x => x.CreatedAt) @@ -268,7 +268,7 @@ public async Task GetUserChainDetailAsync_WhenChainBelongsToDifferentUser_Throws ownerRoot, Now.AddMinutes(-10)); - await store.CreateChainAsync(chain); + await store.CreateChainAsync(chain, ct: TestContext.Current.CancellationToken); var act = () => sut.GetUserChainDetailAsync( Context(requestedUser), @@ -291,7 +291,7 @@ public async Task GetUserChainDetailAsync_ReturnsSessionsNewestFirst() root, Now.AddHours(-1)); - await store.CreateChainAsync(chain); + await store.CreateChainAsync(chain, ct: TestContext.Current.CancellationToken); var older = CreateSession( user, @@ -305,13 +305,13 @@ public async Task GetUserChainDetailAsync_ReturnsSessionsNewestFirst() Now.AddMinutes(-10), "newer-session"); - await store.CreateSessionAsync(older); - await store.CreateSessionAsync(newer); + await store.CreateSessionAsync(older, ct: TestContext.Current.CancellationToken); + await store.CreateSessionAsync(newer, ct: TestContext.Current.CancellationToken); var result = await sut.GetUserChainDetailAsync( Context(user), user, - chain.ChainId); + chain.ChainId, ct: TestContext.Current.CancellationToken); result.ChainId.Should().Be(chain.ChainId); @@ -345,16 +345,16 @@ public async Task RevokeUserSessionAsync_WhenSessionBelongsToUser_RevokesSession Now.AddMinutes(-30), "session-to-revoke"); - await store.CreateChainAsync(chain); - await store.CreateSessionAsync(session); + await store.CreateChainAsync(chain, ct: TestContext.Current.CancellationToken); + await store.CreateSessionAsync(session, ct: TestContext.Current.CancellationToken); await sut.RevokeUserSessionAsync( Context(user), user, - session.SessionId); + session.SessionId, ct: TestContext.Current.CancellationToken); var persisted = - await store.GetSessionAsync(session.SessionId); + await store.GetSessionAsync(session.SessionId, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.IsRevoked.Should().BeTrue(); @@ -381,8 +381,8 @@ public async Task RevokeUserSessionAsync_WhenSessionBelongsToDifferentUser_DoesN Now.AddMinutes(-30), "foreign-session"); - await store.CreateChainAsync(chain); - await store.CreateSessionAsync(session); + await store.CreateChainAsync(chain, ct: TestContext.Current.CancellationToken); + await store.CreateSessionAsync(session, ct: TestContext.Current.CancellationToken); var act = () => sut.RevokeUserSessionAsync( Context(caller), @@ -391,7 +391,7 @@ public async Task RevokeUserSessionAsync_WhenSessionBelongsToDifferentUser_DoesN await act.Should().ThrowAsync(); - var persisted = await store.GetSessionAsync(session.SessionId); + var persisted = await store.GetSessionAsync(session.SessionId, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.IsRevoked.Should().BeFalse(); } @@ -412,18 +412,18 @@ public async Task RevokeUserChainAsync_WhenChainBelongsToUser_RevokesChain() root, Now.AddHours(-1)); - await store.CreateChainAsync(chain); + await store.CreateChainAsync(chain, ct: TestContext.Current.CancellationToken); var result = await sut.RevokeUserChainAsync( Context(user), user, - chain.ChainId); + chain.ChainId, ct: TestContext.Current.CancellationToken); result.CurrentChain.Should().BeFalse(); result.RootRevoked.Should().BeFalse(); var persisted = - await store.GetChainAsync(chain.ChainId); + await store.GetChainAsync(chain.ChainId, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.IsRevoked.Should().BeTrue(); @@ -442,18 +442,18 @@ public async Task RevokeUserChainAsync_WhenRevokingActorChain_ReportsCurrentChai root, Now.AddHours(-1)); - await store.CreateChainAsync(chain); + await store.CreateChainAsync(chain, ct: TestContext.Current.CancellationToken); var result = await sut.RevokeUserChainAsync( Context(user, chain.ChainId), user, - chain.ChainId); + chain.ChainId, ct: TestContext.Current.CancellationToken); result.CurrentChain.Should().BeTrue(); result.RootRevoked.Should().BeFalse(); var persisted = - await store.GetChainAsync(chain.ChainId); + await store.GetChainAsync(chain.ChainId, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.IsRevoked.Should().BeTrue(); @@ -487,7 +487,7 @@ public async Task RevokeUserChainAsync_WhenChainBelongsToDifferentUser_MustNotRe ownerRoot, Now.AddHours(-1)); - await store.CreateChainAsync(chain); + await store.CreateChainAsync(chain, ct: TestContext.Current.CancellationToken); var act = () => sut.RevokeUserChainAsync( Context(caller), @@ -498,7 +498,7 @@ await act.Should() .ThrowAsync(); var persisted = - await store.GetChainAsync(chain.ChainId); + await store.GetChainAsync(chain.ChainId, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.IsRevoked.Should().BeFalse(); @@ -528,23 +528,23 @@ public async Task RevokeAllChainsAsync_WithExceptChain_LeavesExceptedChainActive root, Now.AddMinutes(-10)); - await store.CreateChainAsync(keep); - await store.CreateChainAsync(revoke1); - await store.CreateChainAsync(revoke2); + await store.CreateChainAsync(keep, ct: TestContext.Current.CancellationToken); + await store.CreateChainAsync(revoke1, ct: TestContext.Current.CancellationToken); + await store.CreateChainAsync(revoke2, ct: TestContext.Current.CancellationToken); await sut.RevokeAllChainsAsync( Context(user), user, - keep.ChainId); + keep.ChainId, ct: TestContext.Current.CancellationToken); var persistedKeep = - await store.GetChainAsync(keep.ChainId); + await store.GetChainAsync(keep.ChainId, ct: TestContext.Current.CancellationToken); var persistedRevoke1 = - await store.GetChainAsync(revoke1.ChainId); + await store.GetChainAsync(revoke1.ChainId, ct: TestContext.Current.CancellationToken); var persistedRevoke2 = - await store.GetChainAsync(revoke2.ChainId); + await store.GetChainAsync(revoke2.ChainId, ct: TestContext.Current.CancellationToken); persistedKeep!.IsRevoked.Should().BeFalse(); persistedRevoke1!.IsRevoked.Should().BeTrue(); @@ -567,18 +567,18 @@ public async Task RevokeAllChainsAsync_WithoutExceptChain_RevokesAllChains() root, Now.AddMinutes(-10)); - await store.CreateChainAsync(first); - await store.CreateChainAsync(second); + await store.CreateChainAsync(first, ct: TestContext.Current.CancellationToken); + await store.CreateChainAsync(second, ct: TestContext.Current.CancellationToken); await sut.RevokeAllChainsAsync( Context(user), user, - exceptChainId: null); + exceptChainId: null, ct: TestContext.Current.CancellationToken); - (await store.GetChainAsync(first.ChainId))! + (await store.GetChainAsync(first.ChainId, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeTrue(); - (await store.GetChainAsync(second.ChainId))! + (await store.GetChainAsync(second.ChainId, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeTrue(); } @@ -598,11 +598,11 @@ public async Task LogoutDeviceAsync_WhenActorChainMatches_ReportsCurrentChain() root, Now.AddHours(-1)); - await store.CreateChainAsync(chain); + await store.CreateChainAsync(chain, ct: TestContext.Current.CancellationToken); var result = await sut.LogoutDeviceAsync( Context(user, chain.ChainId), - chain.ChainId); + chain.ChainId, ct: TestContext.Current.CancellationToken); result.CurrentChain.Should().BeTrue(); result.RootRevoked.Should().BeFalse(); @@ -624,8 +624,8 @@ public async Task LogoutOtherDevicesAsync_RevokesSessionsOnOtherChains() root, Now.AddHours(-1)); - await store.CreateChainAsync(currentChain); - await store.CreateChainAsync(otherChain); + await store.CreateChainAsync(currentChain, ct: TestContext.Current.CancellationToken); + await store.CreateChainAsync(otherChain, ct: TestContext.Current.CancellationToken); var currentSession = CreateSession( user, @@ -639,19 +639,19 @@ public async Task LogoutOtherDevicesAsync_RevokesSessionsOnOtherChains() Now.AddMinutes(-20), "other-session"); - await store.CreateSessionAsync(currentSession); - await store.CreateSessionAsync(otherSession); + await store.CreateSessionAsync(currentSession, ct: TestContext.Current.CancellationToken); + await store.CreateSessionAsync(otherSession, ct: TestContext.Current.CancellationToken); await sut.LogoutOtherDevicesAsync( Context(user, currentChain.ChainId), user, - currentChain.ChainId); + currentChain.ChainId, ct: TestContext.Current.CancellationToken); var persistedCurrent = - await store.GetSessionAsync(currentSession.SessionId); + await store.GetSessionAsync(currentSession.SessionId, ct: TestContext.Current.CancellationToken); var persistedOther = - await store.GetSessionAsync(otherSession.SessionId); + await store.GetSessionAsync(otherSession.SessionId, ct: TestContext.Current.CancellationToken); persistedCurrent!.IsRevoked.Should().BeFalse(); persistedOther!.IsRevoked.Should().BeTrue(); @@ -673,8 +673,8 @@ public async Task LogoutAllDevicesAsync_RevokesAllUserSessions() root, Now.AddHours(-1)); - await store.CreateChainAsync(firstChain); - await store.CreateChainAsync(secondChain); + await store.CreateChainAsync(firstChain, ct: TestContext.Current.CancellationToken); + await store.CreateChainAsync(secondChain, ct: TestContext.Current.CancellationToken); var firstSession = CreateSession( user, @@ -688,17 +688,17 @@ public async Task LogoutAllDevicesAsync_RevokesAllUserSessions() Now.AddMinutes(-20), "second-session"); - await store.CreateSessionAsync(firstSession); - await store.CreateSessionAsync(secondSession); + await store.CreateSessionAsync(firstSession, ct: TestContext.Current.CancellationToken); + await store.CreateSessionAsync(secondSession, ct: TestContext.Current.CancellationToken); await sut.LogoutAllDevicesAsync( Context(user), - user); + user, ct: TestContext.Current.CancellationToken); - (await store.GetSessionAsync(firstSession.SessionId))! + (await store.GetSessionAsync(firstSession.SessionId, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeTrue(); - (await store.GetSessionAsync(secondSession.SessionId))! + (await store.GetSessionAsync(secondSession.SessionId, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeTrue(); } @@ -718,23 +718,23 @@ public async Task RevokeRootAsync_RevokesRootAndItsChains() var secondChain = CreateChain(root, Now.AddMinutes(-10)); - await store.CreateChainAsync(firstChain); - await store.CreateChainAsync(secondChain); + await store.CreateChainAsync(firstChain, ct: TestContext.Current.CancellationToken); + await store.CreateChainAsync(secondChain, ct: TestContext.Current.CancellationToken); - await sut.RevokeRootAsync(Context(user), user); + await sut.RevokeRootAsync(Context(user), user, ct: TestContext.Current.CancellationToken); - var activeRoot = await store.GetActiveRootByUserAsync(user); + var activeRoot = await store.GetActiveRootByUserAsync(user, ct: TestContext.Current.CancellationToken); activeRoot.Should().BeNull(); - var persistedRoot = await store.GetRootByIdAsync(root.RootId); + var persistedRoot = await store.GetRootByIdAsync(root.RootId, ct: TestContext.Current.CancellationToken); persistedRoot.Should().NotBeNull(); persistedRoot!.IsRevoked.Should().BeTrue(); persistedRoot.RevokedAt.Should().Be(Now); - (await store.GetChainAsync(firstChain.ChainId))!.IsRevoked.Should().BeTrue(); - (await store.GetChainAsync(secondChain.ChainId))!.IsRevoked.Should().BeTrue(); + (await store.GetChainAsync(firstChain.ChainId, ct: TestContext.Current.CancellationToken))!.IsRevoked.Should().BeTrue(); + (await store.GetChainAsync(secondChain.ChainId, ct: TestContext.Current.CancellationToken))!.IsRevoked.Should().BeTrue(); } [Fact] @@ -747,8 +747,8 @@ public async Task GetUserChainsAsync_ReturnsTotalSessionCountPerChain() var firstChain = CreateChain(root, Now.AddMinutes(-30)); var secondChain = CreateChain(root, Now.AddMinutes(-20)); - await store.CreateChainAsync(firstChain); - await store.CreateChainAsync(secondChain); + await store.CreateChainAsync(firstChain, ct: TestContext.Current.CancellationToken); + await store.CreateChainAsync(secondChain, ct: TestContext.Current.CancellationToken); var firstSession = CreateSession( user, firstChain.ChainId, Now.AddMinutes(-25), "first-1"); @@ -759,14 +759,14 @@ public async Task GetUserChainsAsync_ReturnsTotalSessionCountPerChain() var secondSession = CreateSession( user, secondChain.ChainId, Now.AddMinutes(-10), "second-1"); - await store.CreateSessionAsync(firstSession); - await store.CreateSessionAsync(rotatedSession); - await store.CreateSessionAsync(secondSession); + await store.CreateSessionAsync(firstSession, ct: TestContext.Current.CancellationToken); + await store.CreateSessionAsync(rotatedSession, ct: TestContext.Current.CancellationToken); + await store.CreateSessionAsync(secondSession, ct: TestContext.Current.CancellationToken); var result = await sut.GetUserChainsAsync( Context(user), user, - new PageRequest { PageNumber = 1, PageSize = 10 }); + new PageRequest { PageNumber = 1, PageSize = 10 }, ct: TestContext.Current.CancellationToken); result.Items.Single(x => x.ChainId == firstChain.ChainId) .SessionCount.Should().Be(2); @@ -783,18 +783,18 @@ public async Task GetUserChainsAsync_RevokedSessionsRemainCounted() var root = await CreateRootAsync(store, user); var chain = CreateChain(root, Now.AddMinutes(-30)); - await store.CreateChainAsync(chain); + await store.CreateChainAsync(chain, ct: TestContext.Current.CancellationToken); var first = CreateSession(user, chain.ChainId, Now.AddMinutes(-20), "revoked-1"); var second = CreateSession(user, chain.ChainId, Now.AddMinutes(-10), "revoked-2"); - await store.CreateSessionAsync(first); - await store.CreateSessionAsync(second); + await store.CreateSessionAsync(first, ct: TestContext.Current.CancellationToken); + await store.CreateSessionAsync(second, ct: TestContext.Current.CancellationToken); - await store.RevokeChainCascadeAsync(chain.ChainId, Now); + await store.RevokeChainCascadeAsync(chain.ChainId, Now, ct: TestContext.Current.CancellationToken); - var result = await sut.GetUserChainsAsync(Context(user), user, new PageRequest { PageNumber = 1, PageSize = 10 }); + var result = await sut.GetUserChainsAsync(Context(user), user, new PageRequest { PageNumber = 1, PageSize = 10 }, ct: TestContext.Current.CancellationToken); result.Items.Single(x => x.ChainId == chain.ChainId).SessionCount.Should().Be(2); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/SessionTouchServiceTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/SessionTouchServiceTests.cs index cae98eea..abf83db7 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/SessionTouchServiceTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/SessionTouchServiceTests.cs @@ -25,7 +25,7 @@ public async Task RefreshAsync_WhenValidationIsInvalid_ReturnsReauthRequired() SessionValidationResult.Invalid(SessionState.Revoked), Policy(), SessionTouchMode.IfNeeded, - Now); + Now, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); result.RequiresReauth.Should().BeTrue(); @@ -50,7 +50,7 @@ public async Task RefreshAsync_WhenChainIdIsMissing_ReturnsReauthRequired() validation, Policy(), SessionTouchMode.IfNeeded, - Now); + Now, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); result.RequiresReauth.Should().BeTrue(); @@ -71,7 +71,7 @@ public async Task RefreshAsync_WhenChainDoesNotExist_ReturnsReauthRequired() validation, Policy(), SessionTouchMode.IfNeeded, - Now); + Now, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); result.RequiresReauth.Should().BeTrue(); @@ -86,7 +86,7 @@ public async Task RefreshAsync_WhenSessionDoesNotExist_ReturnsReauthRequired() var store = factory.Create(validation.Tenant); var chain = CreateChain(validation, Now.AddMinutes(-2)); - await store.CreateChainAsync(chain); + await store.CreateChainAsync(chain, ct: TestContext.Current.CancellationToken); var sut = new SessionTouchService(factory); @@ -94,7 +94,7 @@ public async Task RefreshAsync_WhenSessionDoesNotExist_ReturnsReauthRequired() validation, Policy(), SessionTouchMode.IfNeeded, - Now); + Now, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); result.RequiresReauth.Should().BeTrue(); @@ -109,7 +109,7 @@ public async Task RefreshAsync_WhenChainIsRevoked_ReturnsReauthRequired() var store = factory.Create(validation.Tenant); var revoked = chain.Revoke(Now.AddMinutes(-1)); - await store.SaveChainAsync(revoked, chain.Version); + await store.SaveChainAsync(revoked, chain.Version, ct: TestContext.Current.CancellationToken); var sut = new SessionTouchService(factory); @@ -117,13 +117,13 @@ public async Task RefreshAsync_WhenChainIsRevoked_ReturnsReauthRequired() validation, Policy(), SessionTouchMode.IfNeeded, - Now); + Now, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); result.RequiresReauth.Should().BeTrue(); result.DidTouch.Should().BeFalse(); - var persisted = await store.GetChainAsync(chain.ChainId); + var persisted = await store.GetChainAsync(chain.ChainId, ct: TestContext.Current.CancellationToken); persisted!.IsRevoked.Should().BeTrue(); persisted.TouchCount.Should().Be(revoked.TouchCount); @@ -139,10 +139,10 @@ public async Task RefreshAsync_WhenSessionIsRevoked_ReturnsReauthRequired() var store = factory.Create(validation.Tenant); var revoked = session.Revoke(Now.AddMinutes(-1)); - await store.SaveSessionAsync(revoked, session.Version); + await store.SaveSessionAsync(revoked, session.Version, ct: TestContext.Current.CancellationToken); var result = await new SessionTouchService(factory).RefreshAsync( - validation, Policy(), SessionTouchMode.IfNeeded, Now); + validation, Policy(), SessionTouchMode.IfNeeded, Now, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); result.RequiresReauth.Should().BeTrue(); @@ -158,7 +158,7 @@ await CreateValidSessionAsync( sessionExpiresAt: Now); var result = await new SessionTouchService(factory).RefreshAsync( - validation, Policy(), SessionTouchMode.Force, Now); + validation, Policy(), SessionTouchMode.Force, Now, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); result.RequiresReauth.Should().BeTrue(); @@ -175,7 +175,7 @@ public async Task RefreshAsync_WhenChainIdleTimeoutElapsed_ReturnsReauthRequired validation, Policy(idleTimeout: TimeSpan.FromMinutes(10)), SessionTouchMode.Force, - Now); + Now, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); result.RequiresReauth.Should().BeTrue(); @@ -189,15 +189,15 @@ public async Task RefreshAsync_WhenTouchIntervalHasNotElapsed_ReturnsSuccessWith await CreateValidSessionAsync(Now.AddMinutes(-4)); var result = await new SessionTouchService(factory).RefreshAsync( - validation, Policy(), SessionTouchMode.IfNeeded, Now); + validation, Policy(), SessionTouchMode.IfNeeded, Now, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); result.RequiresReauth.Should().BeFalse(); result.DidTouch.Should().BeFalse(); var store = factory.Create(validation.Tenant); - var persistedChain = await store.GetChainAsync(chain.ChainId); - var persistedSession = await store.GetSessionAsync(session.SessionId); + var persistedChain = await store.GetChainAsync(chain.ChainId, ct: TestContext.Current.CancellationToken); + var persistedSession = await store.GetSessionAsync(session.SessionId, ct: TestContext.Current.CancellationToken); persistedChain!.LastSeenAt.Should().Be(chain.LastSeenAt); persistedChain.TouchCount.Should().Be(0); @@ -214,14 +214,14 @@ public async Task RefreshAsync_WhenTouchIntervalHasElapsed_TouchesAndSavesChain( await CreateValidSessionAsync(Now.AddMinutes(-6)); var result = await new SessionTouchService(factory).RefreshAsync( - validation, Policy(), SessionTouchMode.IfNeeded, Now); + validation, Policy(), SessionTouchMode.IfNeeded, Now, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); result.DidTouch.Should().BeTrue(); result.SessionId.Should().Be(validation.SessionId); var persisted = await factory.Create(validation.Tenant) - .GetChainAsync(chain.ChainId); + .GetChainAsync(chain.ChainId, ct: TestContext.Current.CancellationToken); persisted!.LastSeenAt.Should().Be(Now); persisted.TouchCount.Should().Be(1); @@ -235,13 +235,13 @@ public async Task RefreshAsync_WhenTouchIntervalExactlyElapsed_TouchesChain() await CreateValidSessionAsync(Now.AddMinutes(-5)); var result = await new SessionTouchService(factory).RefreshAsync( - validation, Policy(), SessionTouchMode.IfNeeded, Now); + validation, Policy(), SessionTouchMode.IfNeeded, Now, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); result.DidTouch.Should().BeTrue(); var persisted = await factory.Create(validation.Tenant) - .GetChainAsync(chain.ChainId); + .GetChainAsync(chain.ChainId, ct: TestContext.Current.CancellationToken); persisted!.LastSeenAt.Should().Be(Now); persisted.TouchCount.Should().Be(1); @@ -256,13 +256,13 @@ public async Task RefreshAsync_WhenTouchIntervalIsNull_TouchesEveryRefresh() var policy = PolicyWithoutTouchInterval(); var result = await new SessionTouchService(factory).RefreshAsync( - validation, policy, SessionTouchMode.IfNeeded, Now); + validation, policy, SessionTouchMode.IfNeeded, Now, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); result.DidTouch.Should().BeTrue(); var persisted = await factory.Create(validation.Tenant) - .GetChainAsync(chain.ChainId); + .GetChainAsync(chain.ChainId, ct: TestContext.Current.CancellationToken); persisted!.LastSeenAt.Should().Be(Now); persisted.TouchCount.Should().Be(1); @@ -275,13 +275,13 @@ public async Task RefreshAsync_WhenForceIsUsed_TouchesBeforeInterval() await CreateValidSessionAsync(Now.AddMinutes(-1)); var result = await new SessionTouchService(factory).RefreshAsync( - validation, Policy(), SessionTouchMode.Force, Now); + validation, Policy(), SessionTouchMode.Force, Now, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); result.DidTouch.Should().BeTrue(); var persisted = await factory.Create(validation.Tenant) - .GetChainAsync(chain.ChainId); + .GetChainAsync(chain.ChainId, ct: TestContext.Current.CancellationToken); persisted!.LastSeenAt.Should().Be(Now); } @@ -302,14 +302,14 @@ await CreateValidSessionAsync( slidingExpiration: true, lifetime: TimeSpan.FromHours(1)), SessionTouchMode.IfNeeded, - Now); + Now, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); result.DidTouch.Should().BeTrue(); var store = factory.Create(validation.Tenant); - var persistedSession = await store.GetSessionAsync(session.SessionId); - var persistedChain = await store.GetChainAsync(chain.ChainId); + var persistedSession = await store.GetSessionAsync(session.SessionId, ct: TestContext.Current.CancellationToken); + var persistedChain = await store.GetChainAsync(chain.ChainId, ct: TestContext.Current.CancellationToken); persistedSession!.ExpiresAt.Should().Be(Now.AddHours(1)); persistedSession.Version.Should().Be(session.Version + 1); @@ -330,13 +330,13 @@ await CreateValidSessionAsync( validation, Policy(slidingExpiration: false), SessionTouchMode.IfNeeded, - Now); + Now, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); result.DidTouch.Should().BeTrue(); var persisted = await factory.Create(validation.Tenant) - .GetSessionAsync(session.SessionId); + .GetSessionAsync(session.SessionId, ct: TestContext.Current.CancellationToken); persisted!.ExpiresAt.Should().Be(originalExpiry); persisted.Version.Should().Be(session.Version); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/TryLoginTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/TryLoginTests.cs index c8bcf379..56b021fe 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/TryLoginTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/TryLoginTests.cs @@ -25,7 +25,7 @@ public async Task TryLogin_Should_Return_Preview_On_Failure() { Identifier = "user", Secret = "wrong" - }); + }, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result.IsSuccess.Should().BeFalse(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/UAuthFlowServiceTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/UAuthFlowServiceTests.cs index 2a49a93f..f0e8f9f2 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/UAuthFlowServiceTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/UAuthFlowServiceTests.cs @@ -51,7 +51,7 @@ public async Task LoginAsync_DelegatesToLoginOrchestrator() var result = await sut.LoginAsync( flow, - request); + request, ct: TestContext.Current.CancellationToken); result.Should().BeSameAs(expected); @@ -107,7 +107,7 @@ public async Task LoginAsync_WithClientProfileOverride_RecreatesFlowBeforeLogin( var result = await sut.LoginAsync( originalFlow, execution, - request); + request, ct: TestContext.Current.CancellationToken); result.Should().BeSameAs(expected); @@ -171,7 +171,7 @@ public async Task LoginAsync_WithDeviceOverride_RecreatesFlowBeforeLogin() var result = await sut.LoginAsync( originalFlow, execution, - request); + request, ct: TestContext.Current.CancellationToken); result.Should().BeSameAs(expected); @@ -237,7 +237,7 @@ public async Task LoginAsync_WithClientProfileAndDeviceOverride_AppliesOverrides var result = await sut.LoginAsync( originalFlow, execution, - request); + request, ct: TestContext.Current.CancellationToken); result.Should().BeSameAs(expected); @@ -292,7 +292,7 @@ public async Task InternalLoginAsync_DelegatesToInternalLoginOrchestrator() var result = await sut.LoginAsync( flow, request, - options); + options, ct: TestContext.Current.CancellationToken); result.Should().BeSameAs(expected); @@ -364,7 +364,7 @@ public async Task InternalLoginAsync_WithExecutionOverrides_UsesRecreatedFlow() originalFlow, execution, request, - options); + options, ct: TestContext.Current.CancellationToken); result.Should().BeSameAs(expected); } @@ -411,7 +411,7 @@ await sut.LogoutAsync( new LogoutRequest { SessionId = sessionId - }); + }, ct: TestContext.Current.CancellationToken); eventRaised.Should().BeFalse(); } @@ -454,7 +454,7 @@ await sut.LogoutAsync( new LogoutRequest { SessionId = sessionId - }); + }, ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); @@ -506,7 +506,7 @@ await sut.LogoutAsync( new LogoutRequest { SessionId = sessionId - }); + }, ct: TestContext.Current.CancellationToken); eventRaised.Should().BeFalse(); } @@ -583,7 +583,7 @@ await sut.LogoutAllAsync( new LogoutAllRequest { ExceptCurrent = false - }); + }, ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); captured!.UserKey.Should().Be(user); @@ -637,7 +637,7 @@ await sut.LogoutAllAsync( new LogoutAllRequest { ExceptCurrent = true - }); + }, ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); captured!.UserKey.Should().Be(user); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/UAuthResourceAccessOrchestratorTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/UAuthResourceAccessOrchestratorTests.cs index b8cb0db4..6384dbca 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/UAuthResourceAccessOrchestratorTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/UAuthResourceAccessOrchestratorTests.cs @@ -57,7 +57,7 @@ public async Task ExecuteAsync_WhenAllowed_ShouldExecuteCommandExactlyOnce() return Task.CompletedTask; }); - await _sut.ExecuteAsync(context, command); + await _sut.ExecuteAsync(context, command, ct: TestContext.Current.CancellationToken); executions.Should().Be(1); } @@ -179,7 +179,7 @@ public async Task ExecuteAsync_ShouldPassEnrichedContextToPolicyProvider() await _sut.ExecuteAsync( original, - new AccessCommand(_ => Task.CompletedTask)); + new AccessCommand(_ => Task.CompletedTask), ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); @@ -207,7 +207,7 @@ public async Task ExecuteAsync_ShouldCompileWildcardPermissionClaims() await _sut.ExecuteAsync( CreateContext(), - new AccessCommand(_ => Task.CompletedTask)); + new AccessCommand(_ => Task.CompletedTask), ct: TestContext.Current.CancellationToken); var compiled = GetCompiledPermissions(captured!); @@ -233,7 +233,7 @@ public async Task ExecuteAsync_WithNoPermissionClaims_ShouldProvideEmptyPermissi await _sut.ExecuteAsync( CreateContext(), - new AccessCommand(_ => Task.CompletedTask)); + new AccessCommand(_ => Task.CompletedTask), ct: TestContext.Current.CancellationToken); var compiled = GetCompiledPermissions(captured!); @@ -267,7 +267,7 @@ public async Task ExecuteAsync_ShouldPassEnrichedContextToAuthority() await _sut.ExecuteAsync( CreateContext(), - new AccessCommand(_ => Task.CompletedTask)); + new AccessCommand(_ => Task.CompletedTask), ct: TestContext.Current.CancellationToken); authorityContext.Should().NotBeNull(); @@ -291,7 +291,7 @@ public async Task ExecuteAsyncOfT_WhenAllowed_ShouldReturnCommandResult() var command = new AccessCommand( _ => Task.FromResult("result")); - var result = await _sut.ExecuteAsync(context, command); + var result = await _sut.ExecuteAsync(context, command, ct: TestContext.Current.CancellationToken); result.Should().Be("result"); } @@ -441,7 +441,7 @@ public async Task ExecuteAsync_ShouldAwaitAuthorityBeforeExecutingCommand() var executionTask = _sut.ExecuteAsync( CreateContext(), - command); + command, ct: TestContext.Current.CancellationToken); executed.Should().BeFalse(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/UAuthSessionValidatorTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/UAuthSessionValidatorTests.cs index e781ebcf..270ce6ea 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/UAuthSessionValidatorTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Server/UAuthSessionValidatorTests.cs @@ -32,7 +32,7 @@ public async Task ValidateSessionAsync_WhenSessionDoesNotExist_ReturnsNotFound() It.IsAny())) .ReturnsAsync((UAuthSession?)null); - var result = await fixture.Sut.ValidateSessionAsync(fixture.Context); + var result = await fixture.Sut.ValidateSessionAsync(fixture.Context, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.State.Should().Be(SessionState.NotFound); @@ -60,7 +60,7 @@ public async Task ValidateSessionAsync_WhenSessionIsExpired_ReturnsExpired() SetupSession(fixture); - var result = await fixture.Sut.ValidateSessionAsync(fixture.Context); + var result = await fixture.Sut.ValidateSessionAsync(fixture.Context, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.State.Should().Be(SessionState.Expired); @@ -88,7 +88,7 @@ public async Task ValidateSessionAsync_WhenSessionIsRevoked_ReturnsRevoked() It.IsAny())) .ReturnsAsync(revoked); - var result = await fixture.Sut.ValidateSessionAsync(fixture.Context); + var result = await fixture.Sut.ValidateSessionAsync(fixture.Context, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.State.Should().Be(SessionState.Revoked); @@ -120,7 +120,7 @@ public async Task ValidateSessionAsync_WhenChainDoesNotExist_ReturnsRevoked() It.IsAny())) .ReturnsAsync((UAuthSessionChain?)null); - var result = await fixture.Sut.ValidateSessionAsync(fixture.Context); + var result = await fixture.Sut.ValidateSessionAsync(fixture.Context, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.State.Should().Be(SessionState.Revoked); @@ -150,7 +150,7 @@ public async Task ValidateSessionAsync_WhenChainIsRevoked_ReturnsRevoked() It.IsAny())) .ReturnsAsync(revokedChain); - var result = await fixture.Sut.ValidateSessionAsync(fixture.Context); + var result = await fixture.Sut.ValidateSessionAsync(fixture.Context, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.State.Should().Be(SessionState.Revoked); @@ -170,7 +170,7 @@ public async Task ValidateSessionAsync_WhenChainIsAbsolutelyExpired_ReturnsExpir SetupSessionAndChain(fixture); - var result = await fixture.Sut.ValidateSessionAsync(fixture.Context); + var result = await fixture.Sut.ValidateSessionAsync(fixture.Context, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.State.Should().Be(SessionState.Expired); @@ -191,7 +191,7 @@ public async Task ValidateSessionAsync_WhenChainIdleTimeoutIsExceeded_ReturnsExp SetupSessionAndChain(fixture); - var result = await fixture.Sut.ValidateSessionAsync(fixture.Context); + var result = await fixture.Sut.ValidateSessionAsync(fixture.Context, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.State.Should().Be(SessionState.Expired); @@ -220,7 +220,7 @@ public async Task ValidateSessionAsync_WhenRootDoesNotExist_ReturnsRevoked() It.IsAny())) .ReturnsAsync((UAuthSessionRoot?)null); - var result = await fixture.Sut.ValidateSessionAsync(fixture.Context); + var result = await fixture.Sut.ValidateSessionAsync(fixture.Context, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.State.Should().Be(SessionState.Revoked); @@ -249,7 +249,7 @@ public async Task ValidateSessionAsync_WhenRootIsRevoked_ReturnsRevoked() It.IsAny())) .ReturnsAsync(revokedRoot); - var result = await fixture.Sut.ValidateSessionAsync(fixture.Context); + var result = await fixture.Sut.ValidateSessionAsync(fixture.Context, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.State.Should().Be(SessionState.Revoked); @@ -281,7 +281,7 @@ public async Task ValidateSessionAsync_WhenChainBelongsToDifferentRoot_ReturnsSe It.IsAny())) .ReturnsAsync(differentRoot); - var result = await fixture.Sut.ValidateSessionAsync(fixture.Context); + var result = await fixture.Sut.ValidateSessionAsync(fixture.Context, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.State.Should().Be(SessionState.SecurityMismatch); @@ -311,7 +311,7 @@ public async Task ValidateSessionAsync_WhenSecurityVersionDoesNotMatch_ReturnsSe It.IsAny())) .ReturnsAsync(updatedRoot); - var result = await fixture.Sut.ValidateSessionAsync(fixture.Context); + var result = await fixture.Sut.ValidateSessionAsync(fixture.Context, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.State.Should().Be(SessionState.SecurityMismatch); @@ -338,7 +338,7 @@ public async Task ValidateSessionAsync_WhenDeviceDoesNotMatchAndBehaviorIsReject SetupValidAggregate(fixture); - var result = await fixture.Sut.ValidateSessionAsync(fixture.Context); + var result = await fixture.Sut.ValidateSessionAsync(fixture.Context, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.State.Should().Be(SessionState.DeviceMismatch); @@ -373,7 +373,7 @@ public async Task ValidateSessionAsync_WhenRequestHasNoDeviceId_AllowsValidation It.IsAny())) .ReturnsAsync(claims); - var result = await fixture.Sut.ValidateSessionAsync(fixture.Context); + var result = await fixture.Sut.ValidateSessionAsync(fixture.Context, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeTrue(); result.State.Should().Be(SessionState.Active); @@ -401,7 +401,7 @@ public async Task ValidateSessionAsync_WhenAggregateIsValid_ReturnsActiveResult( It.IsAny())) .ReturnsAsync(claims); - var result = await fixture.Sut.ValidateSessionAsync(fixture.Context); + var result = await fixture.Sut.ValidateSessionAsync(fixture.Context, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeTrue(); result.State.Should().Be(SessionState.Active); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/CompositeSessionIdResolverTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/CompositeSessionIdResolverTests.cs index d466699e..c89a0ee3 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/CompositeSessionIdResolverTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/CompositeSessionIdResolverTests.cs @@ -31,7 +31,7 @@ public async Task ResolveAsync_ShouldUseFirstSuccessfulResolverAccordingToConfig bearer.Object, cookie.Object); - var result = await sut.ResolveAsync(context); + var result = await sut.ResolveAsync(context, ct: TestContext.Current.CancellationToken); result.Should().Be(firstId); bearer.Verify(x => x.Resolve(context), Times.Once); @@ -53,7 +53,7 @@ public async Task ResolveAsync_WhenFirstResolverReturnsNull_ShouldContinueToNext bearer.Object, cookie.Object); - var result = await sut.ResolveAsync(context); + var result = await sut.ResolveAsync(context, ct: TestContext.Current.CancellationToken); result.Should().Be(expected); bearer.Verify(x => x.Resolve(context), Times.Once); @@ -77,7 +77,7 @@ public async Task ResolveAsync_WhenResolverIsDisabled_ShouldSkipIt() bearer.Object, cookie.Object); - var result = await sut.ResolveAsync(context); + var result = await sut.ResolveAsync(context, ct: TestContext.Current.CancellationToken); result.Should().Be(cookieId); bearer.Verify(x => x.Resolve(It.IsAny()), Times.Never); @@ -94,7 +94,7 @@ public async Task ResolveAsync_WhenConfiguredResolverDoesNotExist_ShouldContinue var (sut, context) = CreateSut(options, cookie.Object); - var result = await sut.ResolveAsync(context); + var result = await sut.ResolveAsync(context, ct: TestContext.Current.CancellationToken); result.Should().Be(expected); cookie.Verify(x => x.Resolve(context), Times.Once); @@ -110,7 +110,7 @@ public async Task ResolveAsync_WhenOrderUsesDifferentCasing_ShouldResolveCaseIns var (sut, context) = CreateSut(options, bearer.Object); - var result = await sut.ResolveAsync(context); + var result = await sut.ResolveAsync(context, ct: TestContext.Current.CancellationToken); result.Should().Be(expected); bearer.Verify(x => x.Resolve(context), Times.Once); @@ -129,7 +129,7 @@ public async Task ResolveAsync_WhenNoResolverProducesSessionId_ShouldReturnNull( bearer.Object, cookie.Object); - var result = await sut.ResolveAsync(context); + var result = await sut.ResolveAsync(context, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); bearer.Verify(x => x.Resolve(context), Times.Once); @@ -152,7 +152,7 @@ public async Task ResolveAsync_WhenOrderChanges_ShouldRespectNewPrecedence() bearer.Object, cookie.Object); - var result = await sut.ResolveAsync(context); + var result = await sut.ResolveAsync(context, ct: TestContext.Current.CancellationToken); result.Should().Be(cookieId); cookie.Verify(x => x.Resolve(context), Times.Once); @@ -174,7 +174,7 @@ public async Task ResolveAsync_ShouldRespectEffectiveCookieSetting( var (sut, context) = CreateSut(options, cookie.Object); - var result = await sut.ResolveAsync(context); + var result = await sut.ResolveAsync(context, ct: TestContext.Current.CancellationToken); if (effectiveCookieEnabled) { @@ -219,7 +219,7 @@ public async Task ResolveAsync_ShouldReadClientProfileAndUseItsEffectiveOptions( profileReader.Object, effectiveProvider.Object); - var result = await sut.ResolveAsync(context); + var result = await sut.ResolveAsync(context, ct: TestContext.Current.CancellationToken); result.Should().Be(expected); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/SessionTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/SessionTests.cs index cd433dba..c26fe638 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/SessionTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/SessionTests.cs @@ -26,9 +26,9 @@ public async Task Login_should_cleanup_old_sessions_when_limit_exceeded() } var store = runtime.Services.GetRequiredService().Create(TenantKey.Single); - var chains = await store.GetChainsByUserAsync(TestUsers.User); + var chains = await store.GetChainsByUserAsync(TestUsers.User, ct: TestContext.Current.CancellationToken); var chain = chains.First(); - var sessions = await store.GetSessionsByChainAsync(chain.ChainId); + var sessions = await store.GetSessionsByChainAsync(chain.ChainId, ct: TestContext.Current.CancellationToken); sessions.Count.Should().BeLessThanOrEqualTo(3); } @@ -44,17 +44,17 @@ public async Task Logout_device_should_revoke_sessions_but_keep_chain() { Identifier = "user", Secret = "user" - }); + }, ct: TestContext.Current.CancellationToken); var store = runtime.Services.GetRequiredService().Create(TenantKey.Single); - var chainId = await store.GetChainIdBySessionAsync(result.SessionId!.Value); - await store.LogoutChainAsync(chainId!.Value, runtime.Clock.UtcNow); - var chain = await store.GetChainAsync(chainId.Value); + var chainId = await store.GetChainIdBySessionAsync(result.SessionId!.Value, ct: TestContext.Current.CancellationToken); + await store.LogoutChainAsync(chainId!.Value, runtime.Clock.UtcNow, ct: TestContext.Current.CancellationToken); + var chain = await store.GetChainAsync(chainId.Value, ct: TestContext.Current.CancellationToken); chain.Should().NotBeNull(); chain!.ActiveSessionId.Should().BeNull(); - var sessions = await store.GetSessionsByChainAsync(chainId.Value); + var sessions = await store.GetSessionsByChainAsync(chainId.Value, ct: TestContext.Current.CancellationToken); sessions.All(x => x.IsRevoked).Should().BeTrue(); } @@ -74,13 +74,13 @@ public async Task Logout_other_devices_should_keep_current_chain() var store = runtime.Services.GetRequiredService() .Create(TenantKey.Single); - var chains = await store.GetChainsByUserAsync(TestUsers.User); + var chains = await store.GetChainsByUserAsync(TestUsers.User, ct: TestContext.Current.CancellationToken); var current = chains.First(); - await store.RevokeOtherSessionsAsync(current.UserKey, current.ChainId, runtime.Clock.UtcNow); + await store.RevokeOtherSessionsAsync(current.UserKey, current.ChainId, runtime.Clock.UtcNow, ct: TestContext.Current.CancellationToken); - var updatedChains = await store.GetChainsByUserAsync(current.UserKey); + var updatedChains = await store.GetChainsByUserAsync(current.UserKey, ct: TestContext.Current.CancellationToken); updatedChains.Count(x => x.State == SessionChainState.Active).Should().Be(1); } @@ -97,13 +97,13 @@ public async Task Get_chain_detail_should_return_sessions() { Identifier = "user", Secret = "user" - }); + }, ct: TestContext.Current.CancellationToken); var service = runtime.Services.GetRequiredService(); var context = TestAccessContext.ForUser(TestUsers.User, "session.query"); var store = runtime.Services.GetRequiredService().Create(TenantKey.Single); - var chains = await store.GetChainsByUserAsync(TestUsers.User); - var result = await service.GetUserChainDetailAsync(context, chains.First().UserKey, chains.First().ChainId); + var chains = await store.GetChainsByUserAsync(TestUsers.User, ct: TestContext.Current.CancellationToken); + var result = await service.GetUserChainDetailAsync(context, chains.First().UserKey, chains.First().ChainId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result.Sessions.Should().NotBeEmpty(); @@ -121,12 +121,12 @@ public async Task Revoke_chain_should_revoke_all_sessions() { Identifier = "user", Secret = "user" - }); + }, ct: TestContext.Current.CancellationToken); var store = runtime.Services.GetRequiredService().Create(TenantKey.Single); - var chains = await store.GetChainsByUserAsync(TestUsers.User); - await store.RevokeChainCascadeAsync(chains.First().ChainId, runtime.Clock.UtcNow); - var sessions = await store.GetSessionsByChainAsync(chains.First().ChainId); + var chains = await store.GetChainsByUserAsync(TestUsers.User, ct: TestContext.Current.CancellationToken); + await store.RevokeChainCascadeAsync(chains.First().ChainId, runtime.Clock.UtcNow, ct: TestContext.Current.CancellationToken); + var sessions = await store.GetSessionsByChainAsync(chains.First().ChainId, ct: TestContext.Current.CancellationToken); sessions.All(x => x.IsRevoked).Should().BeTrue(); } diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/Store/SessionStoreChainContractTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/Store/SessionStoreChainContractTests.cs index ef08049b..4b632fba 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/Store/SessionStoreChainContractTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/Store/SessionStoreChainContractTests.cs @@ -83,7 +83,7 @@ public async Task CreateChainAsync_PersistsChain() await CreateChainGraphAsync(store, Tenant); var result = - await store.GetChainAsync(chain.ChainId); + await store.GetChainAsync(chain.ChainId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -107,7 +107,7 @@ public async Task GetChainAsync_WhenMissing_ReturnsNull() var result = await store.GetChainAsync( - SessionChainId.New()); + SessionChainId.New(), ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -126,7 +126,7 @@ public async Task CreateChainAsync_WhenVersionIsNotZero_IsRejected() Now); await store.ExecuteAsync( - ct => store.CreateRootAsync(root, ct)); + ct => store.CreateRootAsync(root, ct), ct: TestContext.Current.CancellationToken); var chain = CreateChain( root, @@ -196,11 +196,11 @@ await store.ExecuteAsync( ct => store.SaveChainAsync( updated, expectedVersion: chain.Version, - ct)); + ct), ct: TestContext.Current.CancellationToken); var result = await store.GetChainAsync( - chain.ChainId); + chain.ChainId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.LastSeenAt.Should().Be( @@ -283,11 +283,11 @@ await store.ExecuteAsync( ct => store.RevokeChainAsync( chain.ChainId, Now.AddMinutes(10), - ct)); + ct), ct: TestContext.Current.CancellationToken); var result = await store.GetChainAsync( - chain.ChainId); + chain.ChainId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.IsRevoked.Should().BeTrue(); @@ -326,17 +326,17 @@ await store.ExecuteAsync( ct => store.RevokeChainAsync( chain.ChainId, Now.AddMinutes(5), - ct)); + ct), ct: TestContext.Current.CancellationToken); await store.ExecuteAsync( ct => store.RevokeChainAsync( chain.ChainId, Now.AddMinutes(10), - ct)); + ct), ct: TestContext.Current.CancellationToken); var result = await store.GetChainAsync( - chain.ChainId); + chain.ChainId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.RevokedAt.Should().Be( @@ -372,7 +372,7 @@ await CreateChainGraphAsync( await store.ExecuteAsync( ct => store.CreateChainAsync( secondChainA, - ct)); + ct), ct: TestContext.Current.CancellationToken); var (_, chainB) = await CreateChainGraphAsync( @@ -382,7 +382,7 @@ await CreateChainGraphAsync( var result = await store.GetChainsByRootAsync( - rootA.RootId); + rootA.RootId, ct: TestContext.Current.CancellationToken); result.Should().HaveCount(2); @@ -421,10 +421,10 @@ await CreateChainGraphAsync( await store.ExecuteAsync( ct => store.CreateChainAsync( second, - ct)); + ct), ct: TestContext.Current.CancellationToken); var result = - await store.GetChainsByUserAsync(user); + await store.GetChainsByUserAsync(user, ct: TestContext.Current.CancellationToken); result.Select(x => x.ChainId) .Should() @@ -459,11 +459,11 @@ await CreateChainGraphAsync( var fromA = await storeA.GetChainAsync( - chain.ChainId); + chain.ChainId, ct: TestContext.Current.CancellationToken); var fromB = await storeB.GetChainAsync( - chain.ChainId); + chain.ChainId, ct: TestContext.Current.CancellationToken); fromA.Should().NotBeNull(); fromB.Should().BeNull(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/Store/SessionStoreLifecycleContractTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/Store/SessionStoreLifecycleContractTests.cs index 9d6eca75..3dccddd6 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/Store/SessionStoreLifecycleContractTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/Store/SessionStoreLifecycleContractTests.cs @@ -230,13 +230,13 @@ public async Task Graph_HasExpectedActiveSessions() var graph = await CreateGraphAsync(store); var chainA1 = await store.GetChainAsync( - graph.ChainA1.ChainId); + graph.ChainA1.ChainId, ct: TestContext.Current.CancellationToken); var chainA2 = await store.GetChainAsync( - graph.ChainA2.ChainId); + graph.ChainA2.ChainId, ct: TestContext.Current.CancellationToken); var chainB1 = await store.GetChainAsync( - graph.ChainB1.ChainId); + graph.ChainB1.ChainId, ct: TestContext.Current.CancellationToken); chainA1!.ActiveSessionId .Should().Be(graph.SessionA12.SessionId); @@ -266,10 +266,10 @@ await store.ExecuteAsync( ct => store.RevokeSessionAsync( graph.SessionA11.SessionId, revokedAt, - ct)); + ct), ct: TestContext.Current.CancellationToken); var session = await store.GetSessionAsync( - graph.SessionA11.SessionId); + graph.SessionA11.SessionId, ct: TestContext.Current.CancellationToken); session.Should().NotBeNull(); session!.IsRevoked.Should().BeTrue(); @@ -288,10 +288,10 @@ await store.ExecuteAsync( ct => store.RevokeSessionAsync( graph.SessionA11.SessionId, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var chain = await store.GetChainAsync( - graph.ChainA1.ChainId); + graph.ChainA1.ChainId, ct: TestContext.Current.CancellationToken); chain!.ActiveSessionId .Should().Be(graph.SessionA12.SessionId); @@ -309,13 +309,13 @@ await store.ExecuteAsync( ct => store.RevokeSessionAsync( graph.SessionA12.SessionId, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var session = await store.GetSessionAsync( - graph.SessionA12.SessionId); + graph.SessionA12.SessionId, ct: TestContext.Current.CancellationToken); var chain = await store.GetChainAsync( - graph.ChainA1.ChainId); + graph.ChainA1.ChainId, ct: TestContext.Current.CancellationToken); session.Should().NotBeNull(); session!.IsRevoked.Should().BeTrue(); @@ -337,13 +337,13 @@ await store.ExecuteAsync( ct => store.RevokeSessionAsync( graph.SessionA12.SessionId, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var a21 = await store.GetSessionAsync( - graph.SessionA21.SessionId); + graph.SessionA21.SessionId, ct: TestContext.Current.CancellationToken); var b11 = await store.GetSessionAsync( - graph.SessionB11.SessionId); + graph.SessionB11.SessionId, ct: TestContext.Current.CancellationToken); a21!.IsRevoked.Should().BeFalse(); b11!.IsRevoked.Should().BeFalse(); @@ -364,10 +364,10 @@ public async Task RemoveSessionAsync_WhenSessionIsNotActive_RemovesSession() await store.ExecuteAsync( ct => store.RemoveSessionAsync( graph.SessionA11.SessionId, - ct)); + ct), ct: TestContext.Current.CancellationToken); var session = await store.GetSessionAsync( - graph.SessionA11.SessionId); + graph.SessionA11.SessionId, ct: TestContext.Current.CancellationToken); session.Should().BeNull(); } @@ -383,10 +383,10 @@ public async Task RemoveSessionAsync_WhenSessionIsNotActive_PreservesActiveSessi await store.ExecuteAsync( ct => store.RemoveSessionAsync( graph.SessionA11.SessionId, - ct)); + ct), ct: TestContext.Current.CancellationToken); var chain = await store.GetChainAsync( - graph.ChainA1.ChainId); + graph.ChainA1.ChainId, ct: TestContext.Current.CancellationToken); chain!.ActiveSessionId .Should().Be(graph.SessionA12.SessionId); @@ -403,13 +403,13 @@ public async Task RemoveSessionAsync_WhenSessionIsActive_DoesNotLeaveDanglingRef await store.ExecuteAsync( ct => store.RemoveSessionAsync( graph.SessionA12.SessionId, - ct)); + ct), ct: TestContext.Current.CancellationToken); var session = await store.GetSessionAsync( - graph.SessionA12.SessionId); + graph.SessionA12.SessionId, ct: TestContext.Current.CancellationToken); var chain = await store.GetChainAsync( - graph.ChainA1.ChainId); + graph.ChainA1.ChainId, ct: TestContext.Current.CancellationToken); session.Should().BeNull(); @@ -436,13 +436,13 @@ await store.ExecuteAsync( ct => store.LogoutChainAsync( graph.ChainA1.ChainId, at, - ct)); + ct), ct: TestContext.Current.CancellationToken); var a11 = await store.GetSessionAsync( - graph.SessionA11.SessionId); + graph.SessionA11.SessionId, ct: TestContext.Current.CancellationToken); var a12 = await store.GetSessionAsync( - graph.SessionA12.SessionId); + graph.SessionA12.SessionId, ct: TestContext.Current.CancellationToken); a11!.IsRevoked.Should().BeTrue(); a12!.IsRevoked.Should().BeTrue(); @@ -463,10 +463,10 @@ await store.ExecuteAsync( ct => store.LogoutChainAsync( graph.ChainA1.ChainId, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var chain = await store.GetChainAsync( - graph.ChainA1.ChainId); + graph.ChainA1.ChainId, ct: TestContext.Current.CancellationToken); chain.Should().NotBeNull(); chain!.ActiveSessionId.Should().BeNull(); @@ -484,10 +484,10 @@ await store.ExecuteAsync( ct => store.LogoutChainAsync( graph.ChainA1.ChainId, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var chain = await store.GetChainAsync( - graph.ChainA1.ChainId); + graph.ChainA1.ChainId, ct: TestContext.Current.CancellationToken); chain.Should().NotBeNull(); chain!.IsRevoked.Should().BeFalse(); @@ -505,19 +505,19 @@ await store.ExecuteAsync( ct => store.LogoutChainAsync( graph.ChainA1.ChainId, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var chainA2 = await store.GetChainAsync( - graph.ChainA2.ChainId); + graph.ChainA2.ChainId, ct: TestContext.Current.CancellationToken); var sessionA21 = await store.GetSessionAsync( - graph.SessionA21.SessionId); + graph.SessionA21.SessionId, ct: TestContext.Current.CancellationToken); var chainB1 = await store.GetChainAsync( - graph.ChainB1.ChainId); + graph.ChainB1.ChainId, ct: TestContext.Current.CancellationToken); var sessionB11 = await store.GetSessionAsync( - graph.SessionB11.SessionId); + graph.SessionB11.SessionId, ct: TestContext.Current.CancellationToken); chainA2!.IsRevoked.Should().BeFalse(); chainA2.ActiveSessionId.Should().Be( @@ -550,10 +550,10 @@ await store.ExecuteAsync( ct => store.RevokeChainCascadeAsync( graph.ChainA1.ChainId, at, - ct)); + ct), ct: TestContext.Current.CancellationToken); var chain = await store.GetChainAsync( - graph.ChainA1.ChainId); + graph.ChainA1.ChainId, ct: TestContext.Current.CancellationToken); chain.Should().NotBeNull(); chain!.IsRevoked.Should().BeTrue(); @@ -572,13 +572,13 @@ await store.ExecuteAsync( ct => store.RevokeChainCascadeAsync( graph.ChainA1.ChainId, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var a11 = await store.GetSessionAsync( - graph.SessionA11.SessionId); + graph.SessionA11.SessionId, ct: TestContext.Current.CancellationToken); var a12 = await store.GetSessionAsync( - graph.SessionA12.SessionId); + graph.SessionA12.SessionId, ct: TestContext.Current.CancellationToken); a11!.IsRevoked.Should().BeTrue(); a12!.IsRevoked.Should().BeTrue(); @@ -596,19 +596,19 @@ await store.ExecuteAsync( ct => store.RevokeChainCascadeAsync( graph.ChainA1.ChainId, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var chainA2 = await store.GetChainAsync( - graph.ChainA2.ChainId); + graph.ChainA2.ChainId, ct: TestContext.Current.CancellationToken); var sessionA21 = await store.GetSessionAsync( - graph.SessionA21.SessionId); + graph.SessionA21.SessionId, ct: TestContext.Current.CancellationToken); var chainB1 = await store.GetChainAsync( - graph.ChainB1.ChainId); + graph.ChainB1.ChainId, ct: TestContext.Current.CancellationToken); var sessionB11 = await store.GetSessionAsync( - graph.SessionB11.SessionId); + graph.SessionB11.SessionId, ct: TestContext.Current.CancellationToken); chainA2!.IsRevoked.Should().BeFalse(); sessionA21!.IsRevoked.Should().BeFalse(); @@ -633,16 +633,16 @@ await store.ExecuteAsync( ct => store.RevokeAllSessionsAsync( graph.UserA, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var a11 = await store.GetSessionAsync( - graph.SessionA11.SessionId); + graph.SessionA11.SessionId, ct: TestContext.Current.CancellationToken); var a12 = await store.GetSessionAsync( - graph.SessionA12.SessionId); + graph.SessionA12.SessionId, ct: TestContext.Current.CancellationToken); var a21 = await store.GetSessionAsync( - graph.SessionA21.SessionId); + graph.SessionA21.SessionId, ct: TestContext.Current.CancellationToken); a11!.IsRevoked.Should().BeTrue(); a12!.IsRevoked.Should().BeTrue(); @@ -661,13 +661,13 @@ await store.ExecuteAsync( ct => store.RevokeAllSessionsAsync( graph.UserA, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var chainA1 = await store.GetChainAsync( - graph.ChainA1.ChainId); + graph.ChainA1.ChainId, ct: TestContext.Current.CancellationToken); var chainA2 = await store.GetChainAsync( - graph.ChainA2.ChainId); + graph.ChainA2.ChainId, ct: TestContext.Current.CancellationToken); chainA1!.ActiveSessionId.Should().BeNull(); chainA2!.ActiveSessionId.Should().BeNull(); @@ -685,13 +685,13 @@ await store.ExecuteAsync( ct => store.RevokeAllSessionsAsync( graph.UserA, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var chainB1 = await store.GetChainAsync( - graph.ChainB1.ChainId); + graph.ChainB1.ChainId, ct: TestContext.Current.CancellationToken); var sessionB11 = await store.GetSessionAsync( - graph.SessionB11.SessionId); + graph.SessionB11.SessionId, ct: TestContext.Current.CancellationToken); chainB1!.IsRevoked.Should().BeFalse(); @@ -722,19 +722,19 @@ await store.ExecuteAsync( graph.UserA, graph.ChainA1.ChainId, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var a11 = await store.GetSessionAsync( - graph.SessionA11.SessionId); + graph.SessionA11.SessionId, ct: TestContext.Current.CancellationToken); var a12 = await store.GetSessionAsync( - graph.SessionA12.SessionId); + graph.SessionA12.SessionId, ct: TestContext.Current.CancellationToken); a11!.IsRevoked.Should().BeFalse(); a12!.IsRevoked.Should().BeFalse(); var chainA1 = await store.GetChainAsync( - graph.ChainA1.ChainId); + graph.ChainA1.ChainId, ct: TestContext.Current.CancellationToken); chainA1!.ActiveSessionId .Should().Be(graph.SessionA12.SessionId); @@ -753,10 +753,10 @@ await store.ExecuteAsync( graph.UserA, graph.ChainA1.ChainId, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var a21 = await store.GetSessionAsync( - graph.SessionA21.SessionId); + graph.SessionA21.SessionId, ct: TestContext.Current.CancellationToken); a21.Should().NotBeNull(); a21!.IsRevoked.Should().BeTrue(); @@ -775,13 +775,13 @@ await store.ExecuteAsync( graph.UserA, graph.ChainA1.ChainId, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var kept = await store.GetChainAsync( - graph.ChainA1.ChainId); + graph.ChainA1.ChainId, ct: TestContext.Current.CancellationToken); var affected = await store.GetChainAsync( - graph.ChainA2.ChainId); + graph.ChainA2.ChainId, ct: TestContext.Current.CancellationToken); kept!.ActiveSessionId .Should().Be(graph.SessionA12.SessionId); @@ -803,13 +803,13 @@ await store.ExecuteAsync( graph.UserA, graph.ChainA1.ChainId, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var b11 = await store.GetSessionAsync( - graph.SessionB11.SessionId); + graph.SessionB11.SessionId, ct: TestContext.Current.CancellationToken); var chainB1 = await store.GetChainAsync( - graph.ChainB1.ChainId); + graph.ChainB1.ChainId, ct: TestContext.Current.CancellationToken); b11!.IsRevoked.Should().BeFalse(); @@ -836,13 +836,13 @@ await store.ExecuteAsync( ct => store.RevokeAllChainsAsync( graph.UserA, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var a1 = await store.GetChainAsync( - graph.ChainA1.ChainId); + graph.ChainA1.ChainId, ct: TestContext.Current.CancellationToken); var a2 = await store.GetChainAsync( - graph.ChainA2.ChainId); + graph.ChainA2.ChainId, ct: TestContext.Current.CancellationToken); a1!.IsRevoked.Should().BeTrue(); a2!.IsRevoked.Should().BeTrue(); @@ -860,10 +860,10 @@ await store.ExecuteAsync( ct => store.RevokeAllChainsAsync( graph.UserA, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var b1 = await store.GetChainAsync( - graph.ChainB1.ChainId); + graph.ChainB1.ChainId, ct: TestContext.Current.CancellationToken); b1!.IsRevoked.Should().BeFalse(); } @@ -885,10 +885,10 @@ await store.ExecuteAsync( graph.UserA, graph.ChainA1.ChainId, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var a1 = await store.GetChainAsync( - graph.ChainA1.ChainId); + graph.ChainA1.ChainId, ct: TestContext.Current.CancellationToken); a1.Should().NotBeNull(); a1!.IsRevoked.Should().BeFalse(); @@ -907,10 +907,10 @@ await store.ExecuteAsync( graph.UserA, graph.ChainA1.ChainId, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var a2 = await store.GetChainAsync( - graph.ChainA2.ChainId); + graph.ChainA2.ChainId, ct: TestContext.Current.CancellationToken); a2.Should().NotBeNull(); a2!.IsRevoked.Should().BeTrue(); @@ -929,10 +929,10 @@ await store.ExecuteAsync( graph.UserA, graph.ChainA1.ChainId, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var b1 = await store.GetChainAsync( - graph.ChainB1.ChainId); + graph.ChainB1.ChainId, ct: TestContext.Current.CancellationToken); b1!.IsRevoked.Should().BeFalse(); } @@ -955,17 +955,17 @@ await store.ExecuteAsync( ct => store.RevokeRootCascadeAsync( graph.UserA, at, - ct)); + ct), ct: TestContext.Current.CancellationToken); var activeRoot = await store.GetActiveRootByUserAsync( - graph.UserA); + graph.UserA, ct: TestContext.Current.CancellationToken); activeRoot.Should().BeNull(); var root = await store.GetRootByIdAsync( - graph.RootA.RootId); + graph.RootA.RootId, ct: TestContext.Current.CancellationToken); root.Should().NotBeNull(); @@ -985,17 +985,17 @@ await store.ExecuteAsync( ct => store.RevokeRootCascadeAsync( graph.UserA, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var activeRoot = await store.GetActiveRootByUserAsync( - graph.UserA); + graph.UserA, ct: TestContext.Current.CancellationToken); activeRoot.Should().BeNull(); var root = await store.GetRootByIdAsync( - graph.RootA.RootId); + graph.RootA.RootId, ct: TestContext.Current.CancellationToken); root.Should().NotBeNull(); @@ -1018,13 +1018,13 @@ await store.ExecuteAsync( ct => store.RevokeRootCascadeAsync( graph.UserA, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var a1 = await store.GetChainAsync( - graph.ChainA1.ChainId); + graph.ChainA1.ChainId, ct: TestContext.Current.CancellationToken); var a2 = await store.GetChainAsync( - graph.ChainA2.ChainId); + graph.ChainA2.ChainId, ct: TestContext.Current.CancellationToken); a1!.IsRevoked.Should().BeTrue(); a2!.IsRevoked.Should().BeTrue(); @@ -1042,16 +1042,16 @@ await store.ExecuteAsync( ct => store.RevokeRootCascadeAsync( graph.UserA, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var a11 = await store.GetSessionAsync( - graph.SessionA11.SessionId); + graph.SessionA11.SessionId, ct: TestContext.Current.CancellationToken); var a12 = await store.GetSessionAsync( - graph.SessionA12.SessionId); + graph.SessionA12.SessionId, ct: TestContext.Current.CancellationToken); var a21 = await store.GetSessionAsync( - graph.SessionA21.SessionId); + graph.SessionA21.SessionId, ct: TestContext.Current.CancellationToken); a11!.IsRevoked.Should().BeTrue(); a12!.IsRevoked.Should().BeTrue(); @@ -1070,16 +1070,16 @@ await store.ExecuteAsync( ct => store.RevokeRootCascadeAsync( graph.UserA, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var rootB = await store.GetActiveRootByUserAsync( - graph.UserB); + graph.UserB, ct: TestContext.Current.CancellationToken); var chainB1 = await store.GetChainAsync( - graph.ChainB1.ChainId); + graph.ChainB1.ChainId, ct: TestContext.Current.CancellationToken); var sessionB11 = await store.GetSessionAsync( - graph.SessionB11.SessionId); + graph.SessionB11.SessionId, ct: TestContext.Current.CancellationToken); rootB.Should().NotBeNull(); chainB1.Should().NotBeNull(); @@ -1109,10 +1109,10 @@ await store.ExecuteAsync( ct => store.LogoutChainAsync( graph.ChainA1.ChainId, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var sessions = await store.GetSessionsByChainAsync( - graph.ChainA1.ChainId); + graph.ChainA1.ChainId, ct: TestContext.Current.CancellationToken); sessions.Should().HaveCount(2); sessions.Should().OnlyContain(x => x.IsRevoked); @@ -1130,11 +1130,11 @@ await store.ExecuteAsync( ct => store.RevokeRootCascadeAsync( graph.UserA, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var chains = await store.GetChainsByUserAsync( graph.UserA, - includeHistoricalRoots: true); + includeHistoricalRoots: true, ct: TestContext.Current.CancellationToken); chains.Should().HaveCount(2); chains.Should().OnlyContain(x => x.IsRevoked); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/Store/SessionStoreRootContractTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/Store/SessionStoreRootContractTests.cs index 91a75392..4f2fc72e 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/Store/SessionStoreRootContractTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/Store/SessionStoreRootContractTests.cs @@ -50,10 +50,10 @@ public async Task CreateRootAsync_PersistsRoot() Now); await store.ExecuteAsync( - ct => store.CreateRootAsync(root, ct)); + ct => store.CreateRootAsync(root, ct), ct: TestContext.Current.CancellationToken); var result = - await store.GetActiveRootByUserAsync(user); + await store.GetActiveRootByUserAsync(user, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -81,7 +81,7 @@ public async Task GetRootByUserAsync_WhenMissing_ReturnsNull() var result = await store.GetActiveRootByUserAsync( - UserKey.New()); + UserKey.New(), ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -99,7 +99,7 @@ await CreateRootAsync( var result = await store.GetRootByIdAsync( - root.RootId); + root.RootId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -122,7 +122,7 @@ public async Task GetRootByIdAsync_WhenMissing_ReturnsNull() var result = await store.GetRootByIdAsync( - missingRoot.RootId); + missingRoot.RootId, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -205,11 +205,11 @@ await store.ExecuteAsync( ct => store.SaveRootAsync( updated, expectedVersion: root.Version, - ct)); + ct), ct: TestContext.Current.CancellationToken); var result = await store.GetActiveRootByUserAsync( - root.UserKey); + root.UserKey, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -302,19 +302,19 @@ await store.ExecuteAsync( ct => store.RevokeRootAsync( root.UserKey, revokedAt, - ct)); + ct), ct: TestContext.Current.CancellationToken); // Revoked root is no longer active. var active = await store.GetActiveRootByUserAsync( - root.UserKey); + root.UserKey, ct: TestContext.Current.CancellationToken); active.Should().BeNull(); // But the historical root must still exist. var result = await store.GetRootByIdAsync( - root.RootId); + root.RootId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -364,11 +364,11 @@ await store.ExecuteAsync( ct => store.RevokeRootAsync( root.UserKey, firstRevokedAt, - ct)); + ct), ct: TestContext.Current.CancellationToken); var afterFirst = await store.GetRootByIdAsync( - root.RootId); + root.RootId, ct: TestContext.Current.CancellationToken); afterFirst.Should().NotBeNull(); afterFirst!.IsRevoked.Should().BeTrue(); @@ -377,11 +377,11 @@ await store.ExecuteAsync( ct => store.RevokeRootAsync( root.UserKey, Now.AddMinutes(10), - ct)); + ct), ct: TestContext.Current.CancellationToken); var afterSecond = await store.GetRootByIdAsync( - root.RootId); + root.RootId, ct: TestContext.Current.CancellationToken); afterSecond.Should().NotBeNull(); afterSecond!.IsRevoked.Should().BeTrue(); @@ -428,10 +428,10 @@ await CreateRootAsync( user); var fromA = - await storeA.GetActiveRootByUserAsync(user); + await storeA.GetActiveRootByUserAsync(user, ct: TestContext.Current.CancellationToken); var fromB = - await storeB.GetActiveRootByUserAsync(user); + await storeB.GetActiveRootByUserAsync(user, ct: TestContext.Current.CancellationToken); fromA.Should().NotBeNull(); fromA!.RootId.Should().Be(root.RootId); @@ -460,11 +460,11 @@ await CreateRootAsync( var fromA = await storeA.GetRootByIdAsync( - root.RootId); + root.RootId, ct: TestContext.Current.CancellationToken); var fromB = await storeB.GetRootByIdAsync( - root.RootId); + root.RootId, ct: TestContext.Current.CancellationToken); fromA.Should().NotBeNull(); fromB.Should().BeNull(); @@ -554,7 +554,7 @@ await store.RevokeRootCascadeAsync( user, Now.AddMinutes(1), ct); - }); + }, ct: TestContext.Current.CancellationToken); var secondRoot = UAuthSessionRoot.Create( Tenant, @@ -566,17 +566,17 @@ await store.ExecuteAsync(async ct => await store.CreateRootAsync( secondRoot, ct); - }); + }, ct: TestContext.Current.CancellationToken); var active = - await store.GetActiveRootByUserAsync(user); + await store.GetActiveRootByUserAsync(user, ct: TestContext.Current.CancellationToken); active.Should().NotBeNull(); active!.RootId.Should().Be(secondRoot.RootId); active.IsRevoked.Should().BeFalse(); var historical = - await store.GetRootByIdAsync(firstRoot.RootId); + await store.GetRootByIdAsync(firstRoot.RootId, ct: TestContext.Current.CancellationToken); historical.Should().NotBeNull(); historical!.IsRevoked.Should().BeTrue(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/Store/SessionStoreSessionContractTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/Store/SessionStoreSessionContractTests.cs index f0323c8d..03ba7bf2 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/Store/SessionStoreSessionContractTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/Store/SessionStoreSessionContractTests.cs @@ -119,7 +119,7 @@ await CreateSessionGraphAsync( Tenant); var result = await store.GetSessionAsync( - session.SessionId); + session.SessionId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -141,7 +141,7 @@ public async Task GetSessionAsync_WhenSessionDoesNotExist_ReturnsNull() var store = db.CreateStore(Tenant); var result = await store.GetSessionAsync( - NewSessionId()); + NewSessionId(), ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -221,11 +221,11 @@ await store.ExecuteAsync( ct => store.SaveSessionAsync( updated, expectedVersion: session.Version, - ct)); + ct), ct: TestContext.Current.CancellationToken); var result = await store.GetSessionAsync( - session.SessionId); + session.SessionId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.IsRevoked.Should().BeTrue(); @@ -306,12 +306,12 @@ await store.ExecuteAsync(async ct => session.SessionId, Now.AddMinutes(10), ct); - }); + }, ct: TestContext.Current.CancellationToken); revoked.Should().BeTrue(); var result = await store.GetSessionAsync( - session.SessionId); + session.SessionId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.IsRevoked.Should().BeTrue(); @@ -333,7 +333,7 @@ await store.ExecuteAsync(async ct => NewSessionId(), Now, ct); - }); + }, ct: TestContext.Current.CancellationToken); result.Should().BeFalse(); } @@ -353,7 +353,7 @@ await store.ExecuteAsync( ct => store.RevokeSessionAsync( session.SessionId, Now.AddMinutes(5), - ct)); + ct), ct: TestContext.Current.CancellationToken); bool secondResult = true; @@ -364,13 +364,13 @@ await store.RevokeSessionAsync( session.SessionId, Now.AddMinutes(10), ct); - }); + }, ct: TestContext.Current.CancellationToken); secondResult.Should().BeFalse(); var result = await store.GetSessionAsync( - session.SessionId); + session.SessionId, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.RevokedAt.Should().Be( @@ -396,11 +396,11 @@ await CreateSessionGraphAsync( await store.ExecuteAsync( ct => store.RemoveSessionAsync( session.SessionId, - ct)); + ct), ct: TestContext.Current.CancellationToken); var result = await store.GetSessionAsync( - session.SessionId); + session.SessionId, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -437,7 +437,7 @@ await CreateSessionGraphAsync( var result = await store.GetChainIdBySessionAsync( - session.SessionId); + session.SessionId, ct: TestContext.Current.CancellationToken); result.Should().Be(chain.ChainId); } @@ -449,7 +449,7 @@ public async Task GetChainIdBySessionAsync_WhenMissing_ReturnsNull() var store = db.CreateStore(Tenant); var result = await store.GetChainIdBySessionAsync( - NewSessionId()); + NewSessionId(), ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -474,10 +474,10 @@ await CreateSessionGraphAsync( chainA.ChainId); await store.ExecuteAsync( - ct => store.CreateSessionAsync(a2, ct)); + ct => store.CreateSessionAsync(a2, ct), ct: TestContext.Current.CancellationToken); var root = - await store.GetActiveRootByUserAsync(user); + await store.GetActiveRootByUserAsync(user, ct: TestContext.Current.CancellationToken); root.Should().NotBeNull(); @@ -493,7 +493,7 @@ await store.ExecuteAsync( root.SecurityVersion); await store.ExecuteAsync( - ct => store.CreateChainAsync(chainB, ct)); + ct => store.CreateChainAsync(chainB, ct), ct: TestContext.Current.CancellationToken); var b1 = CreateSession( Tenant, @@ -501,11 +501,11 @@ await store.ExecuteAsync( chainB.ChainId); await store.ExecuteAsync( - ct => store.CreateSessionAsync(b1, ct)); + ct => store.CreateSessionAsync(b1, ct), ct: TestContext.Current.CancellationToken); var result = await store.GetSessionsByChainAsync( - chainA.ChainId); + chainA.ChainId, ct: TestContext.Current.CancellationToken); result.Should().HaveCount(2); @@ -542,11 +542,11 @@ await CreateSessionGraphAsync( var fromA = await storeA.GetSessionAsync( - session.SessionId); + session.SessionId, ct: TestContext.Current.CancellationToken); var fromB = await storeB.GetSessionAsync( - session.SessionId); + session.SessionId, ct: TestContext.Current.CancellationToken); fromA.Should().NotBeNull(); fromB.Should().BeNull(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/UAuthSessionIssuerTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/UAuthSessionIssuerTests.cs index 04224572..badf5f7c 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/UAuthSessionIssuerTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Sessions/UAuthSessionIssuerTests.cs @@ -103,7 +103,7 @@ public async Task IssueSessionAsync_WhenActiveRootDoesNotExist_ShouldCreateRoot( SetupEmptySessions(fixture); SetupSessionAndChainPersistence(fixture); - await fixture.Sut.IssueSessionAsync(context); + await fixture.Sut.IssueSessionAsync(context, ct: TestContext.Current.CancellationToken); createdRoot.Should().NotBeNull(); createdRoot!.Tenant.Should().Be(context.Tenant); @@ -136,7 +136,7 @@ public async Task IssueSessionAsync_WhenActiveRootExists_ShouldReuseRoot() SetupSessionAndChainPersistence(fixture); var result = - await fixture.Sut.IssueSessionAsync(context); + await fixture.Sut.IssueSessionAsync(context, ct: TestContext.Current.CancellationToken); result.Session.Should().NotBeNull(); @@ -178,7 +178,7 @@ public async Task IssueSessionAsync_WhenChainIdIsNotProvided_ShouldCreateNewChai SetupChainSave(fixture); var result = - await fixture.Sut.IssueSessionAsync(context); + await fixture.Sut.IssueSessionAsync(context, ct: TestContext.Current.CancellationToken); createdChain.Should().NotBeNull(); @@ -227,7 +227,7 @@ public async Task IssueSessionAsync_WhenExistingChainIsProvided_ShouldReuseChain SetupChainSave(fixture); var result = - await fixture.Sut.IssueSessionAsync(context); + await fixture.Sut.IssueSessionAsync(context, ct: TestContext.Current.CancellationToken); result.Session.ChainId.Should() .Be(chain.ChainId); @@ -279,7 +279,7 @@ public async Task IssueSessionAsync_WhenExplicitChainDoesNotExist_CurrentlyCreat SetupChainSave(fixture); var result = - await fixture.Sut.IssueSessionAsync(context); + await fixture.Sut.IssueSessionAsync(context, ct: TestContext.Current.CancellationToken); createdChain.Should().NotBeNull(); @@ -429,7 +429,7 @@ public async Task IssueSessionAsync_ShouldUseConfiguredSessionLifetime() SetupNewIssuance(fixture, context); var result = - await fixture.Sut.IssueSessionAsync(context); + await fixture.Sut.IssueSessionAsync(context, ct: TestContext.Current.CancellationToken); result.Session.ExpiresAt.Should() .Be(Now.AddHours(4)); @@ -447,7 +447,7 @@ public async Task IssueSessionAsync_WhenMaxLifetimeIsShorter_ShouldCapExpiration SetupNewIssuance(fixture, context); var result = - await fixture.Sut.IssueSessionAsync(context); + await fixture.Sut.IssueSessionAsync(context, ct: TestContext.Current.CancellationToken); result.Session.ExpiresAt.Should() .Be(Now.AddHours(2)); @@ -465,7 +465,7 @@ public async Task IssueSessionAsync_WhenMaxLifetimeIsLonger_ShouldUseNormalLifet SetupNewIssuance(fixture, context); var result = - await fixture.Sut.IssueSessionAsync(context); + await fixture.Sut.IssueSessionAsync(context, ct: TestContext.Current.CancellationToken); result.Session.ExpiresAt.Should() .Be(Now.AddHours(4)); @@ -520,7 +520,7 @@ public async Task IssueSessionAsync_ShouldPersistSessionAndAttachItToChain() .Returns(Task.CompletedTask); var result = - await fixture.Sut.IssueSessionAsync(context); + await fixture.Sut.IssueSessionAsync(context, ct: TestContext.Current.CancellationToken); createdSession.Should().NotBeNull(); savedChain.Should().NotBeNull(); @@ -546,7 +546,7 @@ public async Task IssueSessionAsync_WhenModeIsSemiHybrid_ShouldMarkResultAsMetad SetupNewIssuance(fixture, context); var result = - await fixture.Sut.IssueSessionAsync(context); + await fixture.Sut.IssueSessionAsync(context, ct: TestContext.Current.CancellationToken); result.IsMetadataOnly.Should().BeTrue(); } @@ -562,7 +562,7 @@ public async Task IssueSessionAsync_WhenModeIsHybrid_ShouldNotMarkResultAsMetada SetupNewIssuance(fixture, context); var result = - await fixture.Sut.IssueSessionAsync(context); + await fixture.Sut.IssueSessionAsync(context, ct: TestContext.Current.CancellationToken); result.IsMetadataOnly.Should().BeFalse(); } @@ -638,7 +638,7 @@ public async Task IssueSessionAsync_WhenSessionLimitReached_ShouldRemoveOldNonAc (id, _) => removed.Add(id)) .Returns(Task.CompletedTask); - await fixture.Sut.IssueSessionAsync(context); + await fixture.Sut.IssueSessionAsync(context, ct: TestContext.Current.CancellationToken); removed.Should().HaveCount(2); @@ -662,7 +662,7 @@ public async Task IssueSessionAsync_WhenBelowSessionLimit_ShouldNotRemoveSession SetupNewIssuance(fixture, context); - await fixture.Sut.IssueSessionAsync(context); + await fixture.Sut.IssueSessionAsync(context, ct: TestContext.Current.CancellationToken); fixture.Store.Verify( x => x.RemoveSessionAsync( @@ -1060,7 +1060,7 @@ public async Task RotateSessionAsync_ShouldCreateNewSessionOnSameChain() .Returns(Task.CompletedTask); var result = - await fixture.Sut.RotateSessionAsync(context); + await fixture.Sut.RotateSessionAsync(context, ct: TestContext.Current.CancellationToken); persistedSession.Should().NotBeNull(); @@ -1096,7 +1096,7 @@ public async Task RotateSessionAsync_ShouldRotateChainActiveSession() (chain, _, _) => savedChain = chain) .Returns(Task.CompletedTask); - await fixture.Sut.RotateSessionAsync(context); + await fixture.Sut.RotateSessionAsync(context, ct: TestContext.Current.CancellationToken); savedChain.Should().NotBeNull(); @@ -1134,7 +1134,7 @@ public async Task RotateSessionAsync_ShouldRevokeOldSession() }) .Returns(Task.CompletedTask); - await fixture.Sut.RotateSessionAsync(context); + await fixture.Sut.RotateSessionAsync(context, ct: TestContext.Current.CancellationToken); savedOldSession.Should().NotBeNull(); savedOldSession!.IsRevoked.Should().BeTrue(); @@ -1165,7 +1165,7 @@ public async Task RotateSessionAsync_ShouldReturnSameBoundSessionThatWasPersiste persistedSession = session) .Returns(Task.CompletedTask); - var result = await fixture.Sut.RotateSessionAsync(context); + var result = await fixture.Sut.RotateSessionAsync(context, ct: TestContext.Current.CancellationToken); persistedSession.Should().NotBeNull(); persistedSession!.ChainId.Should().Be(setup.Chain.ChainId); @@ -1187,7 +1187,7 @@ public async Task RotateSessionAsync_WhenModeIsSemiHybrid_ShouldMarkResultAsMeta context); var result = - await fixture.Sut.RotateSessionAsync(context); + await fixture.Sut.RotateSessionAsync(context, ct: TestContext.Current.CancellationToken); result.IsMetadataOnly.Should().BeTrue(); } @@ -1215,7 +1215,7 @@ public async Task RotateSessionAsync_WhenMaxLifetimeIsShorter_ShouldCapNewSessio (session, _) => persisted = session) .Returns(Task.CompletedTask); - await fixture.Sut.RotateSessionAsync(context); + await fixture.Sut.RotateSessionAsync(context, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); @@ -1246,7 +1246,7 @@ public async Task RevokeSessionAsync_ShouldDelegateToTenantStoreAndReturnResult( await fixture.Sut.RevokeSessionAsync( Tenant, sessionId, - Now); + Now, ct: TestContext.Current.CancellationToken); result.Should().BeTrue(); @@ -1274,7 +1274,7 @@ public async Task RevokeChainAsync_WhenChainDoesNotExist_ShouldDoNothing() await fixture.Sut.RevokeChainAsync( Tenant, chainId, - Now); + Now, ct: TestContext.Current.CancellationToken); fixture.Store.Verify( x => x.RevokeChainCascadeAsync( @@ -1305,7 +1305,7 @@ public async Task RevokeChainAsync_WhenChainExists_ShouldCascadeRevoke() await fixture.Sut.RevokeChainAsync( Tenant, chain.ChainId, - Now); + Now, ct: TestContext.Current.CancellationToken); fixture.Store.Verify( x => x.RevokeChainCascadeAsync( @@ -1348,7 +1348,7 @@ await fixture.Sut.RevokeAllChainsAsync( Tenant, user, exceptChainId: null, - Now); + Now, ct: TestContext.Current.CancellationToken); fixture.Store.Verify( x => x.RevokeChainCascadeAsync( @@ -1394,7 +1394,7 @@ await fixture.Sut.RevokeAllChainsAsync( Tenant, user, keep.ChainId, - Now); + Now, ct: TestContext.Current.CancellationToken); fixture.Store.Verify( x => x.RevokeChainCascadeAsync( @@ -1431,7 +1431,7 @@ public async Task RevokeRootAsync_ShouldCascadeRevokeRoot() await fixture.Sut.RevokeRootAsync( Tenant, user, - Now); + Now, ct: TestContext.Current.CancellationToken); fixture.Store.Verify( x => x.RevokeRootCascadeAsync( @@ -1465,7 +1465,7 @@ public async Task GetChainIdBySessionAsync_ShouldReturnStoreResult() var result = await fixture.Sut.GetChainIdBySessionAsync( Tenant, - sessionId); + sessionId, ct: TestContext.Current.CancellationToken); result.Should().Be(chainId); } @@ -1490,7 +1490,7 @@ public async Task LogoutChainAsync_WhenChainDoesNotExist_ShouldReturnFalse() await fixture.Sut.LogoutChainAsync( Tenant, chainId, - Now); + Now, ct: TestContext.Current.CancellationToken); result.Should().BeFalse(); @@ -1522,7 +1522,7 @@ public async Task LogoutChainAsync_WhenChainIsRevoked_ShouldReturnFalse() await fixture.Sut.LogoutChainAsync( Tenant, chain.ChainId, - Now); + Now, ct: TestContext.Current.CancellationToken); result.Should().BeFalse(); @@ -1556,7 +1556,7 @@ public async Task LogoutChainAsync_WhenChainIsActive_ShouldLogoutAndReturnTrue() await fixture.Sut.LogoutChainAsync( Tenant, chain.ChainId, - Now); + Now, ct: TestContext.Current.CancellationToken); result.Should().BeTrue(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Tokens/RefreshTokenStoreContractTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Tokens/RefreshTokenStoreContractTests.cs index bf95ebf9..ff3ede7d 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Tokens/RefreshTokenStoreContractTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Tokens/RefreshTokenStoreContractTests.cs @@ -35,10 +35,10 @@ public async Task StoreAsync_PersistsToken() "hash-a11"); await store.ExecuteAsync( - ct => store.StoreAsync(token, ct)); + ct => store.StoreAsync(token, ct), ct: TestContext.Current.CancellationToken); var persisted = await store.FindByHashAsync( - token.TokenHash); + token.TokenHash, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); @@ -60,7 +60,7 @@ public async Task FindByHashAsync_WhenMissing_ReturnsNull() var store = db.CreateStore(TenantA); var result = await store.FindByHashAsync( - "missing-token-hash"); + "missing-token-hash", ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -95,13 +95,13 @@ public async Task FindByHashAsync_IsTenantIsolated() "tenant-isolation-token"); await storeA.ExecuteAsync( - ct => storeA.StoreAsync(token, ct)); + ct => storeA.StoreAsync(token, ct), ct: TestContext.Current.CancellationToken); var fromA = await storeA.FindByHashAsync( - token.TokenHash); + token.TokenHash, ct: TestContext.Current.CancellationToken); var fromB = await storeB.FindByHashAsync( - token.TokenHash); + token.TokenHash, ct: TestContext.Current.CancellationToken); fromA.Should().NotBeNull(); fromB.Should().BeNull(); @@ -129,10 +129,10 @@ await store.ExecuteAsync( ct => store.RevokeAsync( token.TokenHash, revokedAt, - ct: ct)); + ct: ct), ct: TestContext.Current.CancellationToken); var persisted = await store.FindByHashAsync( - token.TokenHash); + token.TokenHash, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.IsRevoked.Should().BeTrue(); @@ -159,10 +159,10 @@ await store.ExecuteAsync( token.TokenHash, revokedAt, "rotation-new", - ct)); + ct), ct: TestContext.Current.CancellationToken); var persisted = await store.FindByHashAsync( - token.TokenHash); + token.TokenHash, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.IsRevoked.Should().BeTrue(); @@ -206,17 +206,17 @@ await store.ExecuteAsync( token.TokenHash, firstRevocation, "replacement-1", - ct)); + ct), ct: TestContext.Current.CancellationToken); await store.ExecuteAsync( ct => store.RevokeAsync( token.TokenHash, Now.AddMinutes(20), "replacement-2", - ct)); + ct), ct: TestContext.Current.CancellationToken); var persisted = await store.FindByHashAsync( - token.TokenHash); + token.TokenHash, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); @@ -250,12 +250,12 @@ public async Task TryConsumeAsync_WhenTokenIsActive_ConsumesTokenAndReturnsTrue( token.TokenHash, consumedAt, "replacement-token", - ct)); + ct), ct: TestContext.Current.CancellationToken); consumed.Should().BeTrue(); var persisted = - await store.FindByHashAsync(token.TokenHash); + await store.FindByHashAsync(token.TokenHash, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.IsRevoked.Should().BeTrue(); @@ -284,7 +284,7 @@ public async Task TryConsumeAsync_WhenTokenAlreadyConsumed_ReturnsFalseAndDoesNo token.TokenHash, firstConsumedAt, "replacement-1", - ct)); + ct), ct: TestContext.Current.CancellationToken); first.Should().BeTrue(); @@ -293,12 +293,12 @@ public async Task TryConsumeAsync_WhenTokenAlreadyConsumed_ReturnsFalseAndDoesNo token.TokenHash, Now.AddMinutes(20), "replacement-2", - ct)); + ct), ct: TestContext.Current.CancellationToken); second.Should().BeFalse(); var persisted = - await store.FindByHashAsync(token.TokenHash); + await store.FindByHashAsync(token.TokenHash, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); @@ -320,7 +320,7 @@ public async Task TryConsumeAsync_WhenTokenDoesNotExist_ReturnsFalse() "missing-token", Now, "replacement-token", - ct)); + ct), ct: TestContext.Current.CancellationToken); result.Should().BeFalse(); } @@ -349,15 +349,15 @@ public async Task TryConsumeAsync_IsTenantIsolated() tokenA.TokenHash, Now.AddMinutes(10), "replacement-a", - ct)); + ct), ct: TestContext.Current.CancellationToken); consumed.Should().BeTrue(); var persistedA = - await storeA.FindByHashAsync(tokenA.TokenHash); + await storeA.FindByHashAsync(tokenA.TokenHash, ct: TestContext.Current.CancellationToken); var persistedB = - await storeB.FindByHashAsync(tokenB.TokenHash); + await storeB.FindByHashAsync(tokenB.TokenHash, ct: TestContext.Current.CancellationToken); persistedA!.IsRevoked.Should().BeTrue(); @@ -387,14 +387,14 @@ public async Task TryConsumeAsync_WhenCalledConcurrently_AllowsExactlyOneConsume token.TokenHash, consumedAt, "replacement-a", - ct)); + ct), ct: TestContext.Current.CancellationToken); var taskB = storeB.ExecuteAsync( ct => storeB.TryConsumeAsync( token.TokenHash, consumedAt, "replacement-b", - ct)); + ct), ct: TestContext.Current.CancellationToken); var results = await Task.WhenAll( taskA, @@ -408,7 +408,7 @@ public async Task TryConsumeAsync_WhenCalledConcurrently_AllowsExactlyOneConsume var persisted = await storeA.FindByHashAsync( - token.TokenHash); + token.TokenHash, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.IsRevoked.Should().BeTrue(); @@ -438,7 +438,7 @@ public async Task TryConsumeAsync_WhenAlreadyConsumed_DoesNotOverwriteWinningRep token.TokenHash, winningTime, "winner-replacement", - ct)); + ct), ct: TestContext.Current.CancellationToken); winner.Should().BeTrue(); @@ -447,12 +447,12 @@ public async Task TryConsumeAsync_WhenAlreadyConsumed_DoesNotOverwriteWinningRep token.TokenHash, Now.AddMinutes(20), "loser-replacement", - ct)); + ct), ct: TestContext.Current.CancellationToken); loser.Should().BeFalse(); var persisted = - await store.FindByHashAsync(token.TokenHash); + await store.FindByHashAsync(token.TokenHash, ct: TestContext.Current.CancellationToken); persisted!.RevokedAt.Should() .Be(winningTime); @@ -495,12 +495,12 @@ await store.ExecuteAsync( ct => store.RevokeBySessionAsync( session, Now.AddMinutes(10), - ct)); + ct), ct: TestContext.Current.CancellationToken); - (await store.FindByHashAsync(token1.TokenHash))! + (await store.FindByHashAsync(token1.TokenHash, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeTrue(); - (await store.FindByHashAsync(token2.TokenHash))! + (await store.FindByHashAsync(token2.TokenHash, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeTrue(); } @@ -539,12 +539,12 @@ await store.ExecuteAsync( ct => store.RevokeBySessionAsync( targetSession, Now.AddMinutes(10), - ct)); + ct), ct: TestContext.Current.CancellationToken); - (await store.FindByHashAsync(target.TokenHash))! + (await store.FindByHashAsync(target.TokenHash, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeTrue(); - (await store.FindByHashAsync(other.TokenHash))! + (await store.FindByHashAsync(other.TokenHash, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeFalse(); } @@ -581,12 +581,12 @@ await store.ExecuteAsync( ct => store.RevokeByChainAsync( chain, Now.AddMinutes(10), - ct)); + ct), ct: TestContext.Current.CancellationToken); - (await store.FindByHashAsync(token1.TokenHash))! + (await store.FindByHashAsync(token1.TokenHash, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeTrue(); - (await store.FindByHashAsync(token2.TokenHash))! + (await store.FindByHashAsync(token2.TokenHash, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeTrue(); } @@ -621,12 +621,12 @@ await store.ExecuteAsync( ct => store.RevokeByChainAsync( targetChain, Now.AddMinutes(10), - ct)); + ct), ct: TestContext.Current.CancellationToken); - (await store.FindByHashAsync(target.TokenHash))! + (await store.FindByHashAsync(target.TokenHash, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeTrue(); - (await store.FindByHashAsync(other.TokenHash))! + (await store.FindByHashAsync(other.TokenHash, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeFalse(); } @@ -662,12 +662,12 @@ await store.ExecuteAsync( ct => store.RevokeAllForUserAsync( user, Now.AddMinutes(10), - ct)); + ct), ct: TestContext.Current.CancellationToken); - (await store.FindByHashAsync(token1.TokenHash))! + (await store.FindByHashAsync(token1.TokenHash, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeTrue(); - (await store.FindByHashAsync(token2.TokenHash))! + (await store.FindByHashAsync(token2.TokenHash, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeTrue(); } @@ -696,12 +696,12 @@ await store.ExecuteAsync( ct => store.RevokeAllForUserAsync( targetUser, Now.AddMinutes(10), - ct)); + ct), ct: TestContext.Current.CancellationToken); - (await store.FindByHashAsync(target.TokenHash))! + (await store.FindByHashAsync(target.TokenHash, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeTrue(); - (await store.FindByHashAsync(other.TokenHash))! + (await store.FindByHashAsync(other.TokenHash, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeFalse(); } @@ -737,13 +737,13 @@ await storeA.ExecuteAsync( ct => storeA.RevokeAsync( tokenA.TokenHash, Now.AddMinutes(10), - ct: ct)); + ct: ct), ct: TestContext.Current.CancellationToken); var persistedA = - await storeA.FindByHashAsync(tokenA.TokenHash); + await storeA.FindByHashAsync(tokenA.TokenHash, ct: TestContext.Current.CancellationToken); var persistedB = - await storeB.FindByHashAsync(tokenB.TokenHash); + await storeB.FindByHashAsync(tokenB.TokenHash, ct: TestContext.Current.CancellationToken); persistedA!.IsRevoked.Should().BeTrue(); persistedB!.IsRevoked.Should().BeFalse(); @@ -777,12 +777,12 @@ await storeA.ExecuteAsync( ct => storeA.RevokeBySessionAsync( session, Now.AddMinutes(10), - ct)); + ct), ct: TestContext.Current.CancellationToken); - (await storeA.FindByHashAsync(tokenA.TokenHash))! + (await storeA.FindByHashAsync(tokenA.TokenHash, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeTrue(); - (await storeB.FindByHashAsync(tokenB.TokenHash))! + (await storeB.FindByHashAsync(tokenB.TokenHash, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeFalse(); } @@ -813,12 +813,12 @@ await storeA.ExecuteAsync( ct => storeA.RevokeByChainAsync( chain, Now.AddMinutes(10), - ct)); + ct), ct: TestContext.Current.CancellationToken); - (await storeA.FindByHashAsync(tokenA.TokenHash))! + (await storeA.FindByHashAsync(tokenA.TokenHash, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeTrue(); - (await storeB.FindByHashAsync(tokenB.TokenHash))! + (await storeB.FindByHashAsync(tokenB.TokenHash, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeFalse(); } @@ -849,12 +849,12 @@ await storeA.ExecuteAsync( ct => storeA.RevokeAllForUserAsync( user, Now.AddMinutes(10), - ct)); + ct), ct: TestContext.Current.CancellationToken); - (await storeA.FindByHashAsync(tokenA.TokenHash))! + (await storeA.FindByHashAsync(tokenA.TokenHash, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeTrue(); - (await storeB.FindByHashAsync(tokenB.TokenHash))! + (await storeB.FindByHashAsync(tokenB.TokenHash, ct: TestContext.Current.CancellationToken))! .IsRevoked.Should().BeFalse(); } @@ -884,16 +884,16 @@ await store.ExecuteAsync( token.TokenHash, firstRevocation, "replacement-token", - ct)); + ct), ct: TestContext.Current.CancellationToken); await store.ExecuteAsync( ct => store.RevokeAllForUserAsync( user, Now.AddMinutes(20), - ct)); + ct), ct: TestContext.Current.CancellationToken); var persisted = - await store.FindByHashAsync(token.TokenHash); + await store.FindByHashAsync(token.TokenHash, ct: TestContext.Current.CancellationToken); persisted!.RevokedAt.Should().Be(firstRevocation); @@ -962,7 +962,7 @@ public async Task StoreAsync_WhenChainIdIsNull_PersistsToken() await StoreAsync(store, token); var persisted = - await store.FindByHashAsync(token.TokenHash); + await store.FindByHashAsync(token.TokenHash, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.ChainId.Should().BeNull(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Tokens/UAuthTokenIssuerTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Tokens/UAuthTokenIssuerTests.cs index 453fe9cc..730fa53c 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Tokens/UAuthTokenIssuerTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Tokens/UAuthTokenIssuerTests.cs @@ -51,7 +51,7 @@ public async Task IssueAccessTokenAsync_WhenModeIsPureOpaque_ShouldIssueOpaqueTo var result = await fixture.Sut.IssueAccessTokenAsync( flow, - context); + context, ct: TestContext.Current.CancellationToken); result.Format.Should().Be(TokenFormat.Opaque); result.Token.Should().Be(OpaqueToken); @@ -79,7 +79,7 @@ public async Task IssueAccessTokenAsync_WhenModeIsHybrid_CurrentlyIssuesOpaqueTo var result = await fixture.Sut.IssueAccessTokenAsync( flow, - context); + context, ct: TestContext.Current.CancellationToken); // Characterizes current Hybrid behavior. // Production code explicitly marks JWT Hybrid as a future decision. @@ -104,7 +104,7 @@ public async Task IssueAccessTokenAsync_WhenOpaque_ShouldUseConfiguredLifetime() var result = await fixture.Sut.IssueAccessTokenAsync( flow, - CreateTokenContext()); + CreateTokenContext(), ct: TestContext.Current.CancellationToken); result.ExpiresAt.Should() .Be(Now.AddMinutes(17)); @@ -128,7 +128,7 @@ public async Task IssueAccessTokenAsync_WhenModeUsesJwt_ShouldIssueJwt( var result = await fixture.Sut.IssueAccessTokenAsync( flow, - context); + context, ct: TestContext.Current.CancellationToken); result.Format.Should().Be(TokenFormat.Jwt); result.Token.Should().Be(Jwt); @@ -169,7 +169,7 @@ public async Task IssueAccessTokenAsync_WhenJwt_ShouldBuildDescriptorFromContext var result = await fixture.Sut.IssueAccessTokenAsync( flow, - context); + context, ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); @@ -227,7 +227,7 @@ public async Task IssueAccessTokenAsync_WhenJwt_ShouldIncludeSubjectAndTenantCla await fixture.Sut.IssueAccessTokenAsync( flow, - context); + context, ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); @@ -270,7 +270,7 @@ public async Task IssueAccessTokenAsync_WhenJwtAndSessionExists_ShouldIncludeSes var result = await fixture.Sut.IssueAccessTokenAsync( flow, - context); + context, ct: TestContext.Current.CancellationToken); captured!.Claims.Should() .ContainKey("sid"); @@ -304,7 +304,7 @@ public async Task IssueAccessTokenAsync_WhenJwtAndSessionDoesNotExist_ShouldNotI var result = await fixture.Sut.IssueAccessTokenAsync( flow, - context); + context, ct: TestContext.Current.CancellationToken); captured!.Claims.Should() .NotContainKey("sid"); @@ -332,7 +332,7 @@ public async Task IssueAccessTokenAsync_WhenJwtIdEnabled_ShouldGenerateJti() await fixture.Sut.IssueAccessTokenAsync( flow, - CreateTokenContext()); + CreateTokenContext(), ct: TestContext.Current.CancellationToken); fixture.OpaqueGenerator.Verify( x => x.GenerateJwtId(), @@ -365,7 +365,7 @@ public async Task IssueAccessTokenAsync_WhenJwtIdDisabled_ShouldNotGenerateJti() await fixture.Sut.IssueAccessTokenAsync( flow, - CreateTokenContext()); + CreateTokenContext(), ct: TestContext.Current.CancellationToken); fixture.OpaqueGenerator.Verify( x => x.GenerateJwtId(), @@ -391,7 +391,7 @@ public async Task IssueRefreshTokenAsync_WhenModeIsPureOpaque_ShouldReturnNull() await fixture.Sut.IssueRefreshTokenAsync( flow, CreateTokenContext(), - RefreshTokenPersistence.Persist); + RefreshTokenPersistence.Persist, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); @@ -426,7 +426,7 @@ public async Task IssueRefreshTokenAsync_WhenSessionIdIsMissing_ShouldReturnNull await fixture.Sut.IssueRefreshTokenAsync( flow, context, - RefreshTokenPersistence.Persist); + RefreshTokenPersistence.Persist, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); @@ -459,7 +459,7 @@ public async Task IssueRefreshTokenAsync_ShouldGenerateAndHashRawToken() var context = CreateTokenContext(sessionId: sessionId); - var result = await fixture.Sut.IssueRefreshTokenAsync(flow, context, RefreshTokenPersistence.DoNotPersist); + var result = await fixture.Sut.IssueRefreshTokenAsync(flow, context, RefreshTokenPersistence.DoNotPersist, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.Token.Should().Be(RefreshToken); @@ -484,7 +484,7 @@ public async Task IssueRefreshTokenAsync_ShouldUseConfiguredRefreshLifetime() await fixture.Sut.IssueRefreshTokenAsync( flow, CreateTokenContext(sessionId: sessionId), - RefreshTokenPersistence.DoNotPersist); + RefreshTokenPersistence.DoNotPersist, ct: TestContext.Current.CancellationToken); result!.ExpiresAt.Should() .Be(Now.AddDays(14)); @@ -525,7 +525,7 @@ public async Task IssueRefreshTokenAsync_WhenPersistenceIsPersist_ShouldStoreHas await fixture.Sut.IssueRefreshTokenAsync( flow, context, - RefreshTokenPersistence.Persist); + RefreshTokenPersistence.Persist, ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); @@ -577,7 +577,7 @@ public async Task IssueRefreshTokenAsync_WhenPersistenceIsDoNotPersist_ShouldNot await fixture.Sut.IssueRefreshTokenAsync( flow, CreateTokenContext(sessionId: sessionId), - RefreshTokenPersistence.DoNotPersist); + RefreshTokenPersistence.DoNotPersist, ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); @@ -606,7 +606,7 @@ public async Task IssueRefreshTokenAsync_WhenPersisting_ShouldExecuteInsideStore await fixture.Sut.IssueRefreshTokenAsync( flow, CreateTokenContext(sessionId: sessionId), - RefreshTokenPersistence.Persist); + RefreshTokenPersistence.Persist, ct: TestContext.Current.CancellationToken); fixture.Store.Verify( x => x.ExecuteAsync( @@ -702,7 +702,7 @@ public async Task IssueAccessTokenAsync_WhenCustomClaimsContainReservedClaims_Sh await fixture.Sut.IssueAccessTokenAsync( flow, - context); + context, ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); @@ -841,7 +841,7 @@ public async Task IssueAccessTokenAsync_WhenMultipleRolesExist_ShouldPreserveAll .Callback(d => captured = d) .Returns(Jwt); - await fixture.Sut.IssueAccessTokenAsync(flow, context); + await fixture.Sut.IssueAccessTokenAsync(flow, context, ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); captured!.Claims.Should().ContainKey("role"); @@ -872,7 +872,7 @@ public async Task IssueAccessTokenAsync_WhenMultiplePermissionsExist_ShouldPrese .Callback(d => captured = d) .Returns(Jwt); - await fixture.Sut.IssueAccessTokenAsync(flow, context); + await fixture.Sut.IssueAccessTokenAsync(flow, context, ct: TestContext.Current.CancellationToken); var permissions = captured!.Claims!["uauth:permission"] .Should().BeAssignableTo().Subject; @@ -898,7 +898,7 @@ public async Task IssueAccessTokenAsync_WhenSingleClaimExists_ShouldUseStringVal .Callback(d => captured = d) .Returns(Jwt); - await fixture.Sut.IssueAccessTokenAsync(flow, context); + await fixture.Sut.IssueAccessTokenAsync(flow, context, ct: TestContext.Current.CancellationToken); captured!.Claims!["department"] .Should().Be("legal"); @@ -926,7 +926,7 @@ public async Task IssueAccessTokenAsync_WhenClaimsAreEmpty_ShouldOnlyIncludeFram descriptor => captured = descriptor) .Returns(Jwt); - await fixture.Sut.IssueAccessTokenAsync(flow, context); + await fixture.Sut.IssueAccessTokenAsync(flow, context, ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); @@ -971,7 +971,7 @@ public async Task IssueAccessTokenAsync_WhenCustomClaimIsReserved_ShouldIgnoreIt .Callback(d => captured = d) .Returns(Jwt); - await fixture.Sut.IssueAccessTokenAsync(flow, context); + await fixture.Sut.IssueAccessTokenAsync(flow, context, ct: TestContext.Current.CancellationToken); captured.Should().NotBeNull(); @@ -1003,7 +1003,7 @@ public async Task IssueAccessTokenAsync_WhenCustomSidExists_ShouldUseFrameworkSe .Callback(d => captured = d) .Returns(Jwt); - await fixture.Sut.IssueAccessTokenAsync(flow, context); + await fixture.Sut.IssueAccessTokenAsync(flow, context, ct: TestContext.Current.CancellationToken); captured!.Claims!["sid"] .Should().Be(sessionId.Value); @@ -1029,7 +1029,7 @@ public async Task IssueAccessTokenAsync_WhenCustomJtiExists_ShouldUseGeneratedJt .Callback(d => captured = d) .Returns(Jwt); - await fixture.Sut.IssueAccessTokenAsync(flow, context); + await fixture.Sut.IssueAccessTokenAsync(flow, context, ct: TestContext.Current.CancellationToken); captured!.Claims!["jti"] .Should().Be(JwtId); @@ -1059,7 +1059,7 @@ public async Task IssueAccessTokenAsync_WhenJwtIdDisabled_ShouldRejectCustomJti( .Callback(d => captured = d) .Returns(Jwt); - await fixture.Sut.IssueAccessTokenAsync(flow, context); + await fixture.Sut.IssueAccessTokenAsync(flow, context, ct: TestContext.Current.CancellationToken); captured!.Claims.Should().NotContainKey("jti"); @@ -1091,10 +1091,10 @@ public async Task IssueTokens_ShouldUseEffectiveLifetimes() "effective-session-0000000000000000000001")); var access = await fixture.Sut.IssueAccessTokenAsync( - flow, context); + flow, context, ct: TestContext.Current.CancellationToken); var refresh = await fixture.Sut.IssueRefreshTokenAsync( - flow, context, RefreshTokenPersistence.DoNotPersist); + flow, context, RefreshTokenPersistence.DoNotPersist, ct: TestContext.Current.CancellationToken); access.ExpiresAt.Should().Be(Now.AddMinutes(5)); refresh.Should().NotBeNull(); @@ -1115,7 +1115,7 @@ public async Task IssueRefreshTokenAsync_WhenIssueRefreshDisabled_ShouldReturnNu var result = await fixture.Sut.IssueRefreshTokenAsync( flow, CreateTokenContext(sessionId: null), - RefreshTokenPersistence.Persist); + RefreshTokenPersistence.Persist, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/IdentifierConcurrencyTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/IdentifierConcurrencyTests.cs index 557935bc..497d72a1 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/IdentifierConcurrencyTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/IdentifierConcurrencyTests.cs @@ -20,15 +20,15 @@ public async Task Save_should_increment_version() var id = Guid.NewGuid(); var identifier = UserIdentifier.Create(id, TenantKey.Single, TestUsers.Admin, UserIdentifierType.Email, "a@test.com", "a@test.com", now); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); - var copy = await store.GetByIdAsync(id); + var copy = await store.GetByIdAsync(id, ct: TestContext.Current.CancellationToken); var expected = copy!.Version; copy.ChangeValue("b@test.com", "b@test.com", now); - await store.SaveAsync(copy, expected); + await store.SaveAsync(copy, expected, ct: TestContext.Current.CancellationToken); - var updated = await store.GetByIdAsync(id); + var updated = await store.GetByIdAsync(id, ct: TestContext.Current.CancellationToken); Assert.Equal(expected + 1, updated!.Version); } @@ -41,18 +41,18 @@ public async Task Delete_should_throw_when_version_conflicts() var id = Guid.NewGuid(); var identifier = UserIdentifier.Create(id, TenantKey.Single, TestUsers.Admin, UserIdentifierType.Email, "a@test.com", "a@test.com", now); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); - var copy1 = await store.GetByIdAsync(id); - var copy2 = await store.GetByIdAsync(id); + var copy1 = await store.GetByIdAsync(id, ct: TestContext.Current.CancellationToken); + var copy2 = await store.GetByIdAsync(id, ct: TestContext.Current.CancellationToken); var expected1 = copy1!.Version; copy1.MarkDeleted(now); - await store.SaveAsync(copy1, expected1); + await store.SaveAsync(copy1, expected1, ct: TestContext.Current.CancellationToken); await Assert.ThrowsAsync(async () => { - await store.DeleteAsync(copy2!.Id, copy2!.Version, DeleteMode.Soft, now); + await store.DeleteAsync(copy2!.Id, copy2!.Version, DeleteMode.Soft, now, ct: TestContext.Current.CancellationToken); }); } @@ -64,7 +64,7 @@ public async Task Parallel_SetPrimary_should_conflict_deterministic() var id = Guid.NewGuid(); var identifier = UserIdentifier.Create(id, TenantKey.Single, TestUsers.Admin, UserIdentifierType.Email, "a@test.com", "a@test.com", now); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); int success = 0; int conflicts = 0; @@ -97,7 +97,7 @@ public async Task Parallel_SetPrimary_should_conflict_deterministic() Assert.Equal(1, success); Assert.Equal(1, conflicts); - var final = await store.GetByIdAsync(id); + var final = await store.GetByIdAsync(id, ct: TestContext.Current.CancellationToken); Assert.True(final!.IsPrimary); Assert.Equal(1, final.Version); } @@ -111,21 +111,21 @@ public async Task Update_should_throw_concurrency_when_versions_conflict() var tenant = TenantKey.Single; var identifier = UserIdentifier.Create(id, TenantKey.Single, TestUsers.Admin, UserIdentifierType.Email, "a@test.com", "a@test.com", now); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); - var copy1 = await store.GetByIdAsync(id); - var copy2 = await store.GetByIdAsync(id); + var copy1 = await store.GetByIdAsync(id, ct: TestContext.Current.CancellationToken); + var copy2 = await store.GetByIdAsync(id, ct: TestContext.Current.CancellationToken); var expected1 = copy1!.Version; copy1.ChangeValue("b@test.com", "b@test.com", now); - await store.SaveAsync(copy1, expected1); + await store.SaveAsync(copy1, expected1, ct: TestContext.Current.CancellationToken); var expected2 = copy2!.Version; copy2.ChangeValue("c@test.com", "c@test.com", now); await Assert.ThrowsAsync(async () => { - await store.SaveAsync(copy2, expected2); + await store.SaveAsync(copy2, expected2, ct: TestContext.Current.CancellationToken); }); } @@ -138,7 +138,7 @@ public async Task Parallel_updates_should_result_in_single_success_deterministic var id = Guid.NewGuid(); var identifier = UserIdentifier.Create(id, TenantKey.Single, TestUsers.Admin, UserIdentifierType.Email, "a@test.com", "a@test.com", now); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); int success = 0; int conflicts = 0; @@ -174,7 +174,7 @@ public async Task Parallel_updates_should_result_in_single_success_deterministic Assert.Equal(1, success); Assert.Equal(1, conflicts); - var final = await store.GetByIdAsync(id); + var final = await store.GetByIdAsync(id, ct: TestContext.Current.CancellationToken); Assert.NotNull(final); Assert.Equal(1, final!.Version); @@ -189,7 +189,7 @@ public async Task High_contention_updates_should_allow_only_one_success() var id = Guid.NewGuid(); var identifier = UserIdentifier.Create(id, TenantKey.Single, TestUsers.Admin, UserIdentifierType.Email, "initial@test.com", "initial@test.com", now); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); int success = 0; int conflicts = 0; @@ -222,7 +222,7 @@ public async Task High_contention_updates_should_allow_only_one_success() Assert.True(success >= 1); Assert.Equal(20, success + conflicts); - var final = await store.GetByIdAsync(id); + var final = await store.GetByIdAsync(id, ct: TestContext.Current.CancellationToken); Assert.Equal(success, final!.Version); } @@ -236,7 +236,7 @@ public async Task High_contention_SetPrimary_should_allow_only_one_deterministic var id = Guid.NewGuid(); var identifier = UserIdentifier.Create(id, TenantKey.Single, TestUsers.Admin, UserIdentifierType.Email, "primary@test.com", "primary@test.com", now); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); int success = 0; int conflicts = 0; @@ -269,7 +269,7 @@ public async Task High_contention_SetPrimary_should_allow_only_one_deterministic Assert.Equal(1, success); Assert.Equal(19, conflicts); - var final = await store.GetByIdAsync(id); + var final = await store.GetByIdAsync(id, ct: TestContext.Current.CancellationToken); Assert.True(final!.IsPrimary); Assert.Equal(1, final.Version); @@ -289,8 +289,8 @@ public async Task Two_identifiers_racing_for_primary_should_allow() var identifier1 = UserIdentifier.Create(id1, TenantKey.Single, TestUsers.Admin, UserIdentifierType.Email, "a@test.com", "a@test.com", now); var identifier2 = UserIdentifier.Create(id2, TenantKey.Single, TestUsers.Admin, UserIdentifierType.Email, "b@test.com", "b@test.com", now); - await store.AddAsync(identifier1); - await store.AddAsync(identifier2); + await store.AddAsync(identifier1, ct: TestContext.Current.CancellationToken); + await store.AddAsync(identifier2, ct: TestContext.Current.CancellationToken); int success = 0; int conflicts = 0; @@ -318,7 +318,7 @@ public async Task Two_identifiers_racing_for_primary_should_allow() { Interlocked.Increment(ref conflicts); } - }), + }, cancellationToken: TestContext.Current.CancellationToken), Task.Run(async () => { try @@ -338,7 +338,7 @@ public async Task Two_identifiers_racing_for_primary_should_allow() { Interlocked.Increment(ref conflicts); } - }) + }, cancellationToken: TestContext.Current.CancellationToken) }; await Task.WhenAll(tasks); @@ -346,7 +346,7 @@ public async Task Two_identifiers_racing_for_primary_should_allow() Assert.Equal(2, success); Assert.Equal(0, conflicts); - var all = await store.GetByUserAsync(user); + var all = await store.GetByUserAsync(user, ct: TestContext.Current.CancellationToken); var primaries = all .Where(x => x.Type == UserIdentifierType.Email && x.IsPrimary) diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/Store/Query/UserSummaryQueryStoreContractTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/Store/Query/UserSummaryQueryStoreContractTests.cs index 860497da..1b8df684 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/Store/Query/UserSummaryQueryStoreContractTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/Store/Query/UserSummaryQueryStoreContractTests.cs @@ -28,7 +28,7 @@ public async Task QueryAsync_WhenEmpty_ReturnsEmptyPage() var store = db.CreateStore(TenantA); - var result = await store.QueryAsync(new UserQuery()); + var result = await store.QueryAsync(new UserQuery(), ct: TestContext.Current.CancellationToken); result.Items.Should().BeEmpty(); result.TotalCount.Should().Be(0); @@ -53,10 +53,10 @@ await db.SeedAsync(TenantB, ]); var resultA = await db.CreateStore(TenantA) - .QueryAsync(new UserQuery()); + .QueryAsync(new UserQuery(), ct: TestContext.Current.CancellationToken); var resultB = await db.CreateStore(TenantB) - .QueryAsync(new UserQuery()); + .QueryAsync(new UserQuery(), ct: TestContext.Current.CancellationToken); resultA.TotalCount.Should().Be(1); resultB.TotalCount.Should().Be(1); @@ -87,7 +87,7 @@ public async Task QueryAsync_With1501Users_ReturnsCorrectLastPage() { PageNumber = 7, PageSize = 250 - }); + }, ct: TestContext.Current.CancellationToken); result.TotalCount.Should().Be(1501); result.Items.Should().ContainSingle(); @@ -119,7 +119,7 @@ public async Task QueryAsync_SearchesBeyondFirst1000Users() { Search = "target@example.com", PageSize = 50 - }); + }, ct: TestContext.Current.CancellationToken); result.TotalCount.Should().Be(1); result.Items.Should().ContainSingle(); @@ -151,7 +151,7 @@ public async Task QueryAsync_SearchesSupportedFields( await db.SeedAsync(TenantA, [target]); var result = await db.CreateStore(TenantA) - .QueryAsync(new UserQuery { Search = search }); + .QueryAsync(new UserQuery { Search = search }, ct: TestContext.Current.CancellationToken); result.TotalCount.Should().Be(1); result.Items.Should().ContainSingle(); @@ -173,7 +173,7 @@ await db.SeedAsync(TenantA, .QueryAsync(new UserQuery { Search = "does-not-exist" - }); + }, ct: TestContext.Current.CancellationToken); result.TotalCount.Should().Be(0); result.Items.Should().BeEmpty(); @@ -195,7 +195,7 @@ await db.SeedAsync(TenantA, ]); var result = await db.CreateStore(TenantA) - .QueryAsync(new UserQuery { Search = search }); + .QueryAsync(new UserQuery { Search = search }, ct: TestContext.Current.CancellationToken); result.TotalCount.Should().Be(2); } @@ -224,7 +224,7 @@ public async Task QueryAsync_PaginationHasNoMissingOrDuplicateUsers() { PageNumber = page, PageSize = 10 - }); + }, ct: TestContext.Current.CancellationToken); result.TotalCount.Should().Be(105); collected.AddRange(result.Items.Select(x => x.UserKey)); @@ -254,7 +254,7 @@ public async Task QueryAsync_PageBeyondLast_ReturnsEmptyButCorrectTotal() { PageNumber = 10, PageSize = 10 - }); + }, ct: TestContext.Current.CancellationToken); result.TotalCount.Should().Be(15); result.Items.Should().BeEmpty(); @@ -276,7 +276,7 @@ await db.SeedAsync(TenantA, { PageNumber = int.MaxValue, PageSize = 1000 - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Should().BeEmpty(); result.TotalCount.Should().Be(1); @@ -304,7 +304,7 @@ public async Task QueryAsync_FiltersByStatus() .QueryAsync(new UserQuery { Status = UserStatus.Active - }); + }, ct: TestContext.Current.CancellationToken); result.TotalCount.Should().Be(1); result.Items.Single().UserKey.Should().Be(active.UserKey); @@ -328,9 +328,9 @@ public async Task QueryAsync_ExcludesDeletedUsersByDefault() var store = db.CreateStore(TenantA); - var normal = await store.QueryAsync(new UserQuery()); + var normal = await store.QueryAsync(new UserQuery(), ct: TestContext.Current.CancellationToken); var includingDeleted = await store.QueryAsync( - new UserQuery { IncludeDeleted = true }); + new UserQuery { IncludeDeleted = true }, ct: TestContext.Current.CancellationToken); normal.TotalCount.Should().Be(1); normal.Items.Single().UserKey.Should().Be(active.UserKey); @@ -352,7 +352,7 @@ public async Task QueryAsync_UserWithoutProfileIsStillReturned() await db.SeedAsync(TenantA, [user]); var result = await db.CreateStore(TenantA) - .QueryAsync(new UserQuery()); + .QueryAsync(new UserQuery(), ct: TestContext.Current.CancellationToken); result.Items.Should().ContainSingle(); result.Items[0].UserKey.Should().Be(user.UserKey); @@ -379,13 +379,13 @@ await db.SeedAsync(TenantA, var store = db.CreateStore(TenantA); var defaultResult = await store.QueryAsync( - new UserQuery()); + new UserQuery(), ct: TestContext.Current.CancellationToken); var businessResult = await store.QueryAsync( new UserQuery { ProfileKey = customProfile - }); + }, ct: TestContext.Current.CancellationToken); defaultResult.TotalCount.Should().Be(1); defaultResult.Items.Single().DisplayName.Should().BeNull(); @@ -415,7 +415,7 @@ public async Task QueryAsync_SearchTreatsSqlWildcardsLiterally( .QueryAsync(new UserQuery { Search = search - }); + }, ct: TestContext.Current.CancellationToken); result.TotalCount.Should().Be(1); result.Items.Single().UserKey.Should().Be(target.UserKey); @@ -457,7 +457,7 @@ await db.SeedAsync(TenantA, .QueryAsync(new UserQuery { Search = key.Value - }); + }, ct: TestContext.Current.CancellationToken); result.TotalCount.Should().Be(1); result.Items.Single().UserKey.Should().Be(key); @@ -479,7 +479,7 @@ await db.SeedAsync(TenantA, .QueryAsync(new UserQuery { Search = "user-key-12345" - }); + }, ct: TestContext.Current.CancellationToken); result.TotalCount.Should().Be(0); result.Items.Should().BeEmpty(); @@ -491,7 +491,7 @@ public async Task GetByKeysAsync_WhenEmpty_ReturnsEmpty() await using var db = await CreateDatabaseAsync(); var result = await db.CreateStore(TenantA) - .GetByKeysAsync(Array.Empty()); + .GetByKeysAsync(Array.Empty(), ct: TestContext.Current.CancellationToken); result.Should().BeEmpty(); } @@ -511,7 +511,7 @@ await db.SeedAsync(TenantA, ]); var result = await db.CreateStore(TenantA) - .GetByKeysAsync([first, first, second, second]); + .GetByKeysAsync([first, first, second, second], ct: TestContext.Current.CancellationToken); result.Should().HaveCount(2); @@ -533,7 +533,7 @@ await db.SeedAsync(TenantA, ]); var result = await db.CreateStore(TenantA) - .GetByKeysAsync([existing, missing]); + .GetByKeysAsync([existing, missing], ct: TestContext.Current.CancellationToken); result.Should().ContainSingle(); @@ -559,7 +559,7 @@ await db.SeedAsync(TenantA, ]); var result = await db.CreateStore(TenantA) - .GetByKeysAsync([active, deleted]); + .GetByKeysAsync([active, deleted], ct: TestContext.Current.CancellationToken); result.Should().ContainSingle(); @@ -595,10 +595,10 @@ await db.SeedAsync(TenantB, }; var resultA = await db.CreateStore(TenantA) - .GetByKeysAsync(requested); + .GetByKeysAsync(requested, ct: TestContext.Current.CancellationToken); var resultB = await db.CreateStore(TenantB) - .GetByKeysAsync(requested); + .GetByKeysAsync(requested, ct: TestContext.Current.CancellationToken); resultA.Should().HaveCount(2); resultB.Should().HaveCount(2); @@ -638,7 +638,7 @@ await db.SeedAsync(TenantA, ]); var result = await db.CreateStore(TenantA) - .GetByKeysAsync([key]); + .GetByKeysAsync([key], ct: TestContext.Current.CancellationToken); var user = result.Should().ContainSingle().Subject; @@ -667,7 +667,7 @@ await db.SeedAsync(TenantA, ]); var result = await db.CreateStore(TenantA) - .GetByKeysAsync([key]); + .GetByKeysAsync([key], ct: TestContext.Current.CancellationToken); result.Should().ContainSingle(); @@ -693,7 +693,7 @@ await db.SeedAsync(TenantA, ]); var result = await db.CreateStore(TenantA) - .GetByKeysAsync([key]); + .GetByKeysAsync([key], ct: TestContext.Current.CancellationToken); result.Should().ContainSingle(); result[0].DisplayName.Should().BeNull(); @@ -718,7 +718,7 @@ public async Task GetByKeysAsync_With250Keys_ReturnsAllUsers() .ToArray(); var result = await db.CreateStore(TenantA) - .GetByKeysAsync(keys); + .GetByKeysAsync(keys, ct: TestContext.Current.CancellationToken); result.Should().HaveCount(250); @@ -748,7 +748,7 @@ public async Task GetByKeysAsync_FindsUsersBeyondFirstLifecyclePage() }; var result = await db.CreateStore(TenantA) - .GetByKeysAsync(requested); + .GetByKeysAsync(requested, ct: TestContext.Current.CancellationToken); result.Should().HaveCount(3); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/Store/UserIdentifierStoreContractTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/Store/UserIdentifierStoreContractTests.cs index 38d11f3a..fa8a4bfc 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/Store/UserIdentifierStoreContractTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/Store/UserIdentifierStoreContractTests.cs @@ -55,9 +55,9 @@ public async Task AddAsync_PersistsIdentifier() TenantA, isPrimary: true); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); - var persisted = await store.GetByIdAsync(identifier.Id); + var persisted = await store.GetByIdAsync(identifier.Id, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.Id.Should().Be(identifier.Id); @@ -77,7 +77,7 @@ public async Task GetByIdAsync_WhenMissing_ReturnsNull() await using var db = await CreateDatabaseAsync(); var store = db.CreateStore(TenantA); - var result = await store.GetByIdAsync(Guid.NewGuid()); + var result = await store.GetByIdAsync(Guid.NewGuid(), ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -90,11 +90,11 @@ public async Task GetAsync_ByTypeAndNormalizedValue_ReturnsIdentifier() var identifier = CreateIdentifier(TenantA); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); var result = await store.GetAsync( UserIdentifierType.Email, - "USER@EXAMPLE.COM"); + "USER@EXAMPLE.COM", ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.Id.Should().Be(identifier.Id); @@ -108,17 +108,17 @@ public async Task GetAsync_ByTypeAndNormalizedValue_ExcludesDeleted() var identifier = CreateIdentifier(TenantA); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( identifier.Id, expectedVersion: 0, DeleteMode.Soft, - Now.AddHours(1)); + Now.AddHours(1), ct: TestContext.Current.CancellationToken); var result = await store.GetAsync( identifier.Type, - identifier.NormalizedValue); + identifier.NormalizedValue, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -151,17 +151,17 @@ public async Task GetByUserAsync_ReturnsOnlyActiveIdentifiersForUser() value: "b@example.com", normalizedValue: "B@EXAMPLE.COM"); - await store.AddAsync(first); - await store.AddAsync(second); - await store.AddAsync(otherUser); + await store.AddAsync(first, ct: TestContext.Current.CancellationToken); + await store.AddAsync(second, ct: TestContext.Current.CancellationToken); + await store.AddAsync(otherUser, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( second.Id, 0, DeleteMode.Soft, - Now.AddHours(1)); + Now.AddHours(1), ct: TestContext.Current.CancellationToken); - var result = await store.GetByUserAsync(userA); + var result = await store.GetByUserAsync(userA, ct: TestContext.Current.CancellationToken); result.Should().ContainSingle(); result.Single().Id.Should().Be(first.Id); @@ -179,7 +179,7 @@ public async Task SaveAsync_PersistsChangesAndIncrementsVersion() var identifier = CreateIdentifier(TenantA); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); var changedAt = Now.AddMinutes(10); @@ -190,9 +190,9 @@ public async Task SaveAsync_PersistsChangesAndIncrementsVersion() await store.SaveAsync( identifier, - expectedVersion: 0); + expectedVersion: 0, ct: TestContext.Current.CancellationToken); - var persisted = await store.GetByIdAsync(identifier.Id); + var persisted = await store.GetByIdAsync(identifier.Id, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.Value.Should().Be("changed@example.com"); @@ -223,10 +223,10 @@ public async Task SaveAsync_WhenVersionIsStale_ThrowsConcurrency() var identifier = CreateIdentifier(TenantA); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); identifier.MarkVerified(Now.AddMinutes(1)); - await store.SaveAsync(identifier, 0); + await store.SaveAsync(identifier, 0, ct: TestContext.Current.CancellationToken); var act = () => store.SaveAsync( identifier, @@ -262,11 +262,11 @@ public async Task AddAsync_PrimaryIdentifier_UnsetsExistingPrimaryOfSameType() normalizedValue: "SECOND@EXAMPLE.COM", isPrimary: true); - await store.AddAsync(first); - await store.AddAsync(second); + await store.AddAsync(first, ct: TestContext.Current.CancellationToken); + await store.AddAsync(second, ct: TestContext.Current.CancellationToken); - var persistedFirst = await store.GetByIdAsync(first.Id); - var persistedSecond = await store.GetByIdAsync(second.Id); + var persistedFirst = await store.GetByIdAsync(first.Id, ct: TestContext.Current.CancellationToken); + var persistedSecond = await store.GetByIdAsync(second.Id, ct: TestContext.Current.CancellationToken); persistedFirst!.IsPrimary.Should().BeFalse(); persistedSecond!.IsPrimary.Should().BeTrue(); @@ -296,13 +296,13 @@ public async Task AddAsync_PrimaryIdentifier_DoesNotUnsetPrimaryOfDifferentType( "ALICE", isPrimary: true); - await store.AddAsync(email); - await store.AddAsync(username); + await store.AddAsync(email, ct: TestContext.Current.CancellationToken); + await store.AddAsync(username, ct: TestContext.Current.CancellationToken); - (await store.GetByIdAsync(email.Id))! + (await store.GetByIdAsync(email.Id, ct: TestContext.Current.CancellationToken))! .IsPrimary.Should().BeTrue(); - (await store.GetByIdAsync(username.Id))! + (await store.GetByIdAsync(username.Id, ct: TestContext.Current.CancellationToken))! .IsPrimary.Should().BeTrue(); } @@ -327,17 +327,17 @@ public async Task SaveAsync_SetPrimary_UnsetsExistingPrimaryOfSameType() value: "second@example.com", normalizedValue: "SECOND@EXAMPLE.COM"); - await store.AddAsync(first); - await store.AddAsync(second); + await store.AddAsync(first, ct: TestContext.Current.CancellationToken); + await store.AddAsync(second, ct: TestContext.Current.CancellationToken); second.SetPrimary(Now.AddMinutes(1)); - await store.SaveAsync(second, 0); + await store.SaveAsync(second, 0, ct: TestContext.Current.CancellationToken); - (await store.GetByIdAsync(first.Id))! + (await store.GetByIdAsync(first.Id, ct: TestContext.Current.CancellationToken))! .IsPrimary.Should().BeFalse(); - (await store.GetByIdAsync(second.Id))! + (await store.GetByIdAsync(second.Id, ct: TestContext.Current.CancellationToken))! .IsPrimary.Should().BeTrue(); } @@ -355,7 +355,7 @@ public async Task DeleteAsync_Soft_PreservesRecordAndClearsPrimary() TenantA, isPrimary: true); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); var deletedAt = Now.AddHours(1); @@ -363,9 +363,9 @@ await store.DeleteAsync( identifier.Id, 0, DeleteMode.Soft, - deletedAt); + deletedAt, ct: TestContext.Current.CancellationToken); - var persisted = await store.GetByIdAsync(identifier.Id); + var persisted = await store.GetByIdAsync(identifier.Id, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.IsDeleted.Should().BeTrue(); @@ -382,15 +382,15 @@ public async Task DeleteAsync_Hard_RemovesIdentifier() var identifier = CreateIdentifier(TenantA); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( identifier.Id, 0, DeleteMode.Hard, - Now); + Now, ct: TestContext.Current.CancellationToken); - (await store.GetByIdAsync(identifier.Id)) + (await store.GetByIdAsync(identifier.Id, ct: TestContext.Current.CancellationToken)) .Should().BeNull(); } @@ -402,10 +402,10 @@ public async Task DeleteAsync_WhenVersionIsStale_ThrowsConcurrency() var identifier = CreateIdentifier(TenantA); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); identifier.MarkVerified(Now.AddMinutes(1)); - await store.SaveAsync(identifier, 0); + await store.SaveAsync(identifier, 0, ct: TestContext.Current.CancellationToken); var act = () => store.DeleteAsync( identifier.Id, @@ -444,7 +444,7 @@ public async Task SaveAsync_WhenEntityBelongsToDifferentTenant_IsRejected() var storeB = db.CreateStore(TenantB); var identifier = CreateIdentifier(TenantB); - await storeB.AddAsync(identifier); + await storeB.AddAsync(identifier, ct: TestContext.Current.CancellationToken); var act = () => storeA.SaveAsync( identifier, @@ -463,9 +463,9 @@ public async Task GetByIdAsync_IsTenantIsolated() var storeB = db.CreateStore(TenantB); var identifier = CreateIdentifier(TenantB); - await storeB.AddAsync(identifier); + await storeB.AddAsync(identifier, ct: TestContext.Current.CancellationToken); - var result = await storeA.GetByIdAsync(identifier.Id); + var result = await storeA.GetByIdAsync(identifier.Id, ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -535,13 +535,13 @@ public async Task ExistsAsync_TenantAny_FindsMatchingIdentifier() var store = db.CreateStore(TenantA); var identifier = CreateIdentifier(TenantA); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); var result = await store.ExistsAsync( new IdentifierExistenceQuery( identifier.Type, identifier.NormalizedValue, - IdentifierExistenceScope.TenantAny)); + IdentifierExistenceScope.TenantAny), ct: TestContext.Current.CancellationToken); result.Exists.Should().BeTrue(); result.OwnerUserKey.Should().Be(identifier.UserKey); @@ -555,14 +555,14 @@ public async Task ExistsAsync_WithinUser_DoesNotMatchOtherUser() var store = db.CreateStore(TenantA); var identifier = CreateIdentifier(TenantA); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); var result = await store.ExistsAsync( new IdentifierExistenceQuery( identifier.Type, identifier.NormalizedValue, IdentifierExistenceScope.WithinUser, - UserKey: UserKey.New())); + UserKey: UserKey.New()), ct: TestContext.Current.CancellationToken); result.Exists.Should().BeFalse(); } @@ -577,13 +577,13 @@ public async Task ExistsAsync_TenantPrimaryOnly_IgnoresNonPrimary() TenantA, isPrimary: false); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); var result = await store.ExistsAsync( new IdentifierExistenceQuery( identifier.Type, identifier.NormalizedValue, - IdentifierExistenceScope.TenantPrimaryOnly)); + IdentifierExistenceScope.TenantPrimaryOnly), ct: TestContext.Current.CancellationToken); result.Exists.Should().BeFalse(); } @@ -595,14 +595,14 @@ public async Task ExistsAsync_ExcludeIdentifierId_ExcludesCurrentIdentifier() var store = db.CreateStore(TenantA); var identifier = CreateIdentifier(TenantA); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); var result = await store.ExistsAsync( new IdentifierExistenceQuery( identifier.Type, identifier.NormalizedValue, IdentifierExistenceScope.TenantAny, - ExcludeIdentifierId: identifier.Id)); + ExcludeIdentifierId: identifier.Id), ct: TestContext.Current.CancellationToken); result.Exists.Should().BeFalse(); } @@ -623,26 +623,26 @@ public async Task ExistsAsync_SoftDeletedIdentifier_RemainsReserved(UserIdentifi normalizedValue: normalizedValue, isPrimary: true); - await store.AddAsync(identifier); + await store.AddAsync(identifier, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( identifier.Id, expectedVersion: 0, DeleteMode.Soft, - Now.AddHours(1)); + Now.AddHours(1), ct: TestContext.Current.CancellationToken); // A deleted identifier still reserves its normalized value. var exists = await store.ExistsAsync( new IdentifierExistenceQuery( type, normalizedValue, - IdentifierExistenceScope.TenantAny)); + IdentifierExistenceScope.TenantAny), ct: TestContext.Current.CancellationToken); exists.Exists.Should().BeTrue(); exists.OwnerIdentifierId.Should().Be(identifier.Id); // Ordinary lookup must not resolve deleted identifiers. - var lookup = await store.GetAsync(type, normalizedValue); + var lookup = await store.GetAsync(type, normalizedValue, ct: TestContext.Current.CancellationToken); lookup.Should().BeNull(); // Excluding the matching identifier must still work. @@ -651,7 +651,7 @@ await store.DeleteAsync( type, normalizedValue, IdentifierExistenceScope.TenantAny, - ExcludeIdentifierId: identifier.Id)); + ExcludeIdentifierId: identifier.Id), ct: TestContext.Current.CancellationToken); excluded.Exists.Should().BeFalse(); } @@ -681,12 +681,12 @@ public async Task GetByUsersAsync_ReturnsActiveIdentifiersForRequestedUsersOnly( value: "c@example.com", normalizedValue: "C@EXAMPLE.COM"); - await store.AddAsync(a); - await store.AddAsync(b); - await store.AddAsync(c); + await store.AddAsync(a, ct: TestContext.Current.CancellationToken); + await store.AddAsync(b, ct: TestContext.Current.CancellationToken); + await store.AddAsync(c, ct: TestContext.Current.CancellationToken); var result = await store.GetByUsersAsync( - [userA, userB]); + [userA, userB], ct: TestContext.Current.CancellationToken); result.Should().HaveCount(2); result.Select(x => x.UserKey) @@ -721,23 +721,23 @@ public async Task DeleteByUserAsync_Soft_DeletesOnlySpecifiedUsersIdentifiers() value: "b@example.com", normalizedValue: "B@EXAMPLE.COM"); - await store.AddAsync(a1); - await store.AddAsync(a2); - await store.AddAsync(b); + await store.AddAsync(a1, ct: TestContext.Current.CancellationToken); + await store.AddAsync(a2, ct: TestContext.Current.CancellationToken); + await store.AddAsync(b, ct: TestContext.Current.CancellationToken); var deletedAt = Now.AddHours(1); await store.DeleteByUserAsync( userA, DeleteMode.Soft, - deletedAt); + deletedAt, ct: TestContext.Current.CancellationToken); var query = await store.QueryAsync( new UserIdentifierQuery { UserKey = userA, IncludeDeleted = true - }); + }, ct: TestContext.Current.CancellationToken); query.Items.Should().HaveCount(2); query.Items.Should().OnlyContain(x => @@ -745,7 +745,7 @@ await store.DeleteByUserAsync( x.DeletedAt == deletedAt && !x.IsPrimary); - (await store.GetByUserAsync(userB)) + (await store.GetByUserAsync(userB, ct: TestContext.Current.CancellationToken)) .Should() .ContainSingle(); } @@ -769,16 +769,16 @@ public async Task DeleteByUserAsync_Hard_RemovesOnlySpecifiedUsersIdentifiers() value: "b@example.com", normalizedValue: "B@EXAMPLE.COM"); - await store.AddAsync(a); - await store.AddAsync(b); + await store.AddAsync(a, ct: TestContext.Current.CancellationToken); + await store.AddAsync(b, ct: TestContext.Current.CancellationToken); await store.DeleteByUserAsync( userA, DeleteMode.Hard, - Now); + Now, ct: TestContext.Current.CancellationToken); - (await store.GetByIdAsync(a.Id)).Should().BeNull(); - (await store.GetByIdAsync(b.Id)).Should().NotBeNull(); + (await store.GetByIdAsync(a.Id, ct: TestContext.Current.CancellationToken)).Should().BeNull(); + (await store.GetByIdAsync(b.Id, ct: TestContext.Current.CancellationToken)).Should().NotBeNull(); } [Fact] @@ -812,20 +812,20 @@ public async Task QueryAsync_ByDefault_ExcludesDeleted() value: "deleted@example.com", normalizedValue: "DELETED@EXAMPLE.COM"); - await store.AddAsync(active); - await store.AddAsync(deleted); + await store.AddAsync(active, ct: TestContext.Current.CancellationToken); + await store.AddAsync(deleted, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( deleted.Id, 0, DeleteMode.Soft, - Now.AddHours(1)); + Now.AddHours(1), ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync( new UserIdentifierQuery { UserKey = user - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Should().ContainSingle(); result.Items.Single().Id.Should().Be(active.Id); @@ -850,21 +850,21 @@ public async Task QueryAsync_IncludeDeleted_ReturnsDeletedIdentifiers() value: "deleted@example.com", normalizedValue: "DELETED@EXAMPLE.COM"); - await store.AddAsync(active); - await store.AddAsync(deleted); + await store.AddAsync(active, ct: TestContext.Current.CancellationToken); + await store.AddAsync(deleted, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( deleted.Id, 0, DeleteMode.Soft, - Now.AddHours(1)); + Now.AddHours(1), ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync( new UserIdentifierQuery { UserKey = user, IncludeDeleted = true - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Should().HaveCount(2); result.TotalCount.Should().Be(2); @@ -886,7 +886,7 @@ await store.AddAsync( user, value: $"user{i}@example.com", normalizedValue: $"USER{i}@EXAMPLE.COM", - createdAt: Now.AddMinutes(i))); + createdAt: Now.AddMinutes(i)), ct: TestContext.Current.CancellationToken); } var result = await store.QueryAsync( @@ -896,7 +896,7 @@ await store.AddAsync( PageNumber = 2, PageSize = 2, SortBy = nameof(UserIdentifier.CreatedAt) - }); + }, ct: TestContext.Current.CancellationToken); result.TotalCount.Should().Be(5); result.Items.Should().HaveCount(2); @@ -918,24 +918,24 @@ public async Task QueryAsync_SortByNormalizedValue_IsSupported() await store.AddAsync(CreateIdentifier( TenantA, user, value: "c@example.com", - normalizedValue: "C@EXAMPLE.COM")); + normalizedValue: "C@EXAMPLE.COM"), ct: TestContext.Current.CancellationToken); await store.AddAsync(CreateIdentifier( TenantA, user, value: "a@example.com", - normalizedValue: "A@EXAMPLE.COM")); + normalizedValue: "A@EXAMPLE.COM"), ct: TestContext.Current.CancellationToken); await store.AddAsync(CreateIdentifier( TenantA, user, value: "b@example.com", - normalizedValue: "B@EXAMPLE.COM")); + normalizedValue: "B@EXAMPLE.COM"), ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync( new UserIdentifierQuery { UserKey = user, SortBy = nameof(UserIdentifier.NormalizedValue) - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Select(x => x.NormalizedValue) .Should() diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/Store/UserLifecycleStoreContractTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/Store/UserLifecycleStoreContractTests.cs index 9b3bb133..ee20b966 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/Store/UserLifecycleStoreContractTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/Store/UserLifecycleStoreContractTests.cs @@ -44,10 +44,10 @@ public async Task AddAsync_PersistsLifecycle() var lifecycle = CreateLifecycle(TenantA); - await store.AddAsync(lifecycle); + await store.AddAsync(lifecycle, ct: TestContext.Current.CancellationToken); var persisted = await store.GetAsync( - new UserLifecycleKey(TenantA, lifecycle.UserKey)); + new UserLifecycleKey(TenantA, lifecycle.UserKey), ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.Id.Should().Be(lifecycle.Id); @@ -66,7 +66,7 @@ public async Task GetAsync_WhenMissing_ReturnsNull() var store = db.CreateStore(TenantA); var result = await store.GetAsync( - new UserLifecycleKey(TenantA, UserKey.New())); + new UserLifecycleKey(TenantA, UserKey.New()), ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -78,10 +78,10 @@ public async Task ExistsAsync_WhenPresent_ReturnsTrue() var store = db.CreateStore(TenantA); var lifecycle = CreateLifecycle(TenantA); - await store.AddAsync(lifecycle); + await store.AddAsync(lifecycle, ct: TestContext.Current.CancellationToken); var result = await store.ExistsAsync( - new UserLifecycleKey(TenantA, lifecycle.UserKey)); + new UserLifecycleKey(TenantA, lifecycle.UserKey), ct: TestContext.Current.CancellationToken); result.Should().BeTrue(); } @@ -93,7 +93,7 @@ public async Task ExistsAsync_WhenMissing_ReturnsFalse() var store = db.CreateStore(TenantA); var result = await store.ExistsAsync( - new UserLifecycleKey(TenantA, UserKey.New())); + new UserLifecycleKey(TenantA, UserKey.New()), ct: TestContext.Current.CancellationToken); result.Should().BeFalse(); } @@ -106,7 +106,7 @@ public async Task AddAsync_WhenSameUserAlreadyExists_ThrowsConflict() var user = UserKey.New(); - await store.AddAsync(CreateLifecycle(TenantA, user)); + await store.AddAsync(CreateLifecycle(TenantA, user), ct: TestContext.Current.CancellationToken); var act = () => store.AddAsync( CreateLifecycle(TenantA, user)); @@ -141,7 +141,7 @@ public async Task SaveAsync_PersistsDomainChanges_AndIncrementsVersion() var store = db.CreateStore(TenantA); var lifecycle = CreateLifecycle(TenantA); - await store.AddAsync(lifecycle); + await store.AddAsync(lifecycle, ct: TestContext.Current.CancellationToken); var changedAt = Now.AddMinutes(10); @@ -153,10 +153,10 @@ public async Task SaveAsync_PersistsDomainChanges_AndIncrementsVersion() await store.SaveAsync( lifecycle, - expectedVersion: 0); + expectedVersion: 0, ct: TestContext.Current.CancellationToken); var persisted = await store.GetAsync( - new UserLifecycleKey(TenantA, lifecycle.UserKey)); + new UserLifecycleKey(TenantA, lifecycle.UserKey), ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.Status.Should().Be(UserStatus.Suspended); @@ -188,11 +188,11 @@ public async Task SaveAsync_WhenVersionIsStale_ThrowsConcurrency() var store = db.CreateStore(TenantA); var lifecycle = CreateLifecycle(TenantA); - await store.AddAsync(lifecycle); + await store.AddAsync(lifecycle, ct: TestContext.Current.CancellationToken); lifecycle.IncrementSecurityVersion(); - await store.SaveAsync(lifecycle, 0); + await store.SaveAsync(lifecycle, 0, ct: TestContext.Current.CancellationToken); lifecycle.IncrementSecurityVersion(); @@ -217,15 +217,15 @@ public async Task DeleteAsync_Soft_PreservesRecordAndDeletionState() var lifecycle = CreateLifecycle(TenantA); var key = new UserLifecycleKey(TenantA, lifecycle.UserKey); - await store.AddAsync(lifecycle); + await store.AddAsync(lifecycle, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( key, expectedVersion: 0, DeleteMode.Soft, - Now.AddHours(1)); + Now.AddHours(1), ct: TestContext.Current.CancellationToken); - var persisted = await store.GetAsync(key); + var persisted = await store.GetAsync(key, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.IsDeleted.Should().BeTrue(); @@ -242,16 +242,16 @@ public async Task DeleteAsync_Hard_RemovesRecord() var lifecycle = CreateLifecycle(TenantA); var key = new UserLifecycleKey(TenantA, lifecycle.UserKey); - await store.AddAsync(lifecycle); + await store.AddAsync(lifecycle, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( key, expectedVersion: 0, DeleteMode.Hard, - Now); + Now, ct: TestContext.Current.CancellationToken); - (await store.GetAsync(key)).Should().BeNull(); - (await store.ExistsAsync(key)).Should().BeFalse(); + (await store.GetAsync(key, ct: TestContext.Current.CancellationToken)).Should().BeNull(); + (await store.ExistsAsync(key, ct: TestContext.Current.CancellationToken)).Should().BeFalse(); } [Fact] @@ -279,10 +279,10 @@ public async Task DeleteAsync_WhenVersionIsStale_ThrowsConcurrency() var lifecycle = CreateLifecycle(TenantA); var key = new UserLifecycleKey(TenantA, lifecycle.UserKey); - await store.AddAsync(lifecycle); + await store.AddAsync(lifecycle, ct: TestContext.Current.CancellationToken); lifecycle.IncrementSecurityVersion(); - await store.SaveAsync(lifecycle, 0); + await store.SaveAsync(lifecycle, 0, ct: TestContext.Current.CancellationToken); var act = () => store.DeleteAsync( key, @@ -321,7 +321,7 @@ public async Task SaveAsync_WhenEntityBelongsToDifferentTenant_IsRejected() var storeB = db.CreateStore(TenantB); var lifecycle = CreateLifecycle(TenantB); - await storeB.AddAsync(lifecycle); + await storeB.AddAsync(lifecycle, ct: TestContext.Current.CancellationToken); var act = () => storeA.SaveAsync( lifecycle, @@ -339,14 +339,14 @@ public async Task QueryAsync_IsTenantIsolated() var storeA = db.CreateStore(TenantA); var storeB = db.CreateStore(TenantB); - await storeA.AddAsync(CreateLifecycle(TenantA)); - await storeB.AddAsync(CreateLifecycle(TenantB)); + await storeA.AddAsync(CreateLifecycle(TenantA), ct: TestContext.Current.CancellationToken); + await storeB.AddAsync(CreateLifecycle(TenantB), ct: TestContext.Current.CancellationToken); var resultA = await storeA.QueryAsync( - new UserLifecycleQuery()); + new UserLifecycleQuery(), ct: TestContext.Current.CancellationToken); var resultB = await storeB.QueryAsync( - new UserLifecycleQuery()); + new UserLifecycleQuery(), ct: TestContext.Current.CancellationToken); resultA.Items.Should().ContainSingle(); resultA.Items.Single().Tenant.Should().Be(TenantA); @@ -368,17 +368,17 @@ public async Task QueryAsync_ByDefault_ExcludesDeleted() var active = CreateLifecycle(TenantA); var deleted = CreateLifecycle(TenantA); - await store.AddAsync(active); - await store.AddAsync(deleted); + await store.AddAsync(active, ct: TestContext.Current.CancellationToken); + await store.AddAsync(deleted, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new UserLifecycleKey(TenantA, deleted.UserKey), 0, DeleteMode.Soft, - Now.AddHours(1)); + Now.AddHours(1), ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync( - new UserLifecycleQuery()); + new UserLifecycleQuery(), ct: TestContext.Current.CancellationToken); result.Items.Should().ContainSingle(); result.Items.Single().UserKey.Should().Be(active.UserKey); @@ -394,20 +394,20 @@ public async Task QueryAsync_IncludeDeleted_ReturnsDeletedRecords() var active = CreateLifecycle(TenantA); var deleted = CreateLifecycle(TenantA); - await store.AddAsync(active); - await store.AddAsync(deleted); + await store.AddAsync(active, ct: TestContext.Current.CancellationToken); + await store.AddAsync(deleted, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new UserLifecycleKey(TenantA, deleted.UserKey), 0, DeleteMode.Soft, - Now.AddHours(1)); + Now.AddHours(1), ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync( new UserLifecycleQuery { IncludeDeleted = true - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Should().HaveCount(2); result.TotalCount.Should().Be(2); @@ -433,14 +433,14 @@ public async Task QueryAsync_Status_FiltersResults() Now.AddMinutes(1), UserStatus.Suspended); - await store.AddAsync(active); - await store.AddAsync(suspended); + await store.AddAsync(active, ct: TestContext.Current.CancellationToken); + await store.AddAsync(suspended, ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync( new UserLifecycleQuery { Status = UserStatus.Suspended - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Should().ContainSingle(); result.Items.Single().UserKey.Should().Be(suspended.UserKey); @@ -462,7 +462,7 @@ public async Task QueryAsync_AppliesPagination() await store.AddAsync( CreateLifecycle( TenantA, - createdAt: Now.AddMinutes(i))); + createdAt: Now.AddMinutes(i)), ct: TestContext.Current.CancellationToken); } var result = await store.QueryAsync( @@ -471,7 +471,7 @@ await store.AddAsync( PageNumber = 2, PageSize = 2, SortBy = nameof(UserLifecycle.CreatedAt) - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Should().HaveCount(2); result.TotalCount.Should().Be(5); @@ -504,16 +504,16 @@ public async Task QueryAsync_SortsCreatedAtAscending() TenantA, createdAt: Now.AddMinutes(2)); - await store.AddAsync(third); - await store.AddAsync(first); - await store.AddAsync(second); + await store.AddAsync(third, ct: TestContext.Current.CancellationToken); + await store.AddAsync(first, ct: TestContext.Current.CancellationToken); + await store.AddAsync(second, ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync( new UserLifecycleQuery { SortBy = nameof(UserLifecycle.CreatedAt), Descending = false - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Select(x => x.CreatedAt) .Should() @@ -530,20 +530,20 @@ public async Task QueryAsync_SortsCreatedAtDescending() var store = db.CreateStore(TenantA); await store.AddAsync( - CreateLifecycle(TenantA, createdAt: Now.AddMinutes(1))); + CreateLifecycle(TenantA, createdAt: Now.AddMinutes(1)), ct: TestContext.Current.CancellationToken); await store.AddAsync( - CreateLifecycle(TenantA, createdAt: Now.AddMinutes(3))); + CreateLifecycle(TenantA, createdAt: Now.AddMinutes(3)), ct: TestContext.Current.CancellationToken); await store.AddAsync( - CreateLifecycle(TenantA, createdAt: Now.AddMinutes(2))); + CreateLifecycle(TenantA, createdAt: Now.AddMinutes(2)), ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync( new UserLifecycleQuery { SortBy = nameof(UserLifecycle.CreatedAt), Descending = true - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Select(x => x.CreatedAt) .Should() @@ -563,16 +563,16 @@ public async Task QueryAsync_SortByUserKey_IsSupported() var secondUser = UserKey.FromString("user-b"); var thirdUser = UserKey.FromString("user-c"); - await store.AddAsync(CreateLifecycle(TenantA, thirdUser)); - await store.AddAsync(CreateLifecycle(TenantA, firstUser)); - await store.AddAsync(CreateLifecycle(TenantA, secondUser)); + await store.AddAsync(CreateLifecycle(TenantA, thirdUser), ct: TestContext.Current.CancellationToken); + await store.AddAsync(CreateLifecycle(TenantA, firstUser), ct: TestContext.Current.CancellationToken); + await store.AddAsync(CreateLifecycle(TenantA, secondUser), ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync( new UserLifecycleQuery { SortBy = nameof(UserLifecycle.UserKey), Descending = false - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Select(x => x.UserKey.Value) .Should() @@ -591,20 +591,20 @@ public async Task QueryAsync_SortByDeletedAt_IsSupported() var first = CreateLifecycle(TenantA); var second = CreateLifecycle(TenantA); - await store.AddAsync(first); - await store.AddAsync(second); + await store.AddAsync(first, ct: TestContext.Current.CancellationToken); + await store.AddAsync(second, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new UserLifecycleKey(TenantA, first.UserKey), 0, DeleteMode.Soft, - Now.AddHours(1)); + Now.AddHours(1), ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new UserLifecycleKey(TenantA, second.UserKey), 0, DeleteMode.Soft, - Now.AddHours(2)); + Now.AddHours(2), ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync( new UserLifecycleQuery @@ -612,7 +612,7 @@ await store.DeleteAsync( IncludeDeleted = true, SortBy = nameof(UserLifecycle.DeletedAt), Descending = false - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Select(x => x.DeletedAt) .Should() diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/Store/UserProfileStoreContractTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/Store/UserProfileStoreContractTests.cs index 522d4c29..eed54630 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/Store/UserProfileStoreContractTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/Store/UserProfileStoreContractTests.cs @@ -59,14 +59,14 @@ public async Task AddAsync_PersistsProfile() var profile = CreateProfile(TenantA); - await store.AddAsync(profile); + await store.AddAsync(profile, ct: TestContext.Current.CancellationToken); var key = new UserProfileKey( TenantA, profile.UserKey, profile.ProfileKey); - var persisted = await store.GetAsync(key); + var persisted = await store.GetAsync(key, ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.Id.Should().Be(profile.Id); @@ -91,7 +91,7 @@ public async Task GetAsync_WhenMissing_ReturnsNull() new UserProfileKey( TenantA, UserKey.New(), - ProfileKey.Default)); + ProfileKey.Default), ct: TestContext.Current.CancellationToken); result.Should().BeNull(); } @@ -103,13 +103,13 @@ public async Task ExistsAsync_WhenPresent_ReturnsTrue() var store = db.CreateStore(TenantA); var profile = CreateProfile(TenantA); - await store.AddAsync(profile); + await store.AddAsync(profile, ct: TestContext.Current.CancellationToken); var result = await store.ExistsAsync( new UserProfileKey( TenantA, profile.UserKey, - profile.ProfileKey)); + profile.ProfileKey), ct: TestContext.Current.CancellationToken); result.Should().BeTrue(); } @@ -124,7 +124,7 @@ public async Task ExistsAsync_WhenMissing_ReturnsFalse() new UserProfileKey( TenantA, UserKey.New(), - ProfileKey.Default)); + ProfileKey.Default), ct: TestContext.Current.CancellationToken); result.Should().BeFalse(); } @@ -138,7 +138,7 @@ public async Task AddAsync_WhenSameUserAndProfileKeyAlreadyExists_ThrowsConflict var user = UserKey.New(); await store.AddAsync( - CreateProfile(TenantA, user)); + CreateProfile(TenantA, user), ct: TestContext.Current.CancellationToken); var duplicate = CreateProfile( TenantA, @@ -171,10 +171,10 @@ public async Task SameUser_CanHaveDifferentProfileKeys() user, secondaryKey); - await store.AddAsync(defaultProfile); - await store.AddAsync(workProfile); + await store.AddAsync(defaultProfile, ct: TestContext.Current.CancellationToken); + await store.AddAsync(workProfile, ct: TestContext.Current.CancellationToken); - var profiles = await store.GetAllProfilesByUserAsync(user); + var profiles = await store.GetAllProfilesByUserAsync(user, ct: TestContext.Current.CancellationToken); profiles.Should().HaveCount(2); profiles.Select(x => x.ProfileKey) @@ -209,7 +209,7 @@ public async Task SaveAsync_PersistsAllMutableFields_AndIncrementsVersion() var profile = CreateProfile(TenantA); - await store.AddAsync(profile); + await store.AddAsync(profile, ct: TestContext.Current.CancellationToken); var changedAt = Now.AddMinutes(10); @@ -241,13 +241,13 @@ public async Task SaveAsync_PersistsAllMutableFields_AndIncrementsVersion() await store.SaveAsync( profile, - expectedVersion: 0); + expectedVersion: 0, ct: TestContext.Current.CancellationToken); var persisted = await store.GetAsync( new UserProfileKey( TenantA, profile.UserKey, - profile.ProfileKey)); + profile.ProfileKey), ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); @@ -294,7 +294,7 @@ public async Task SaveAsync_WhenVersionIsStale_ThrowsConcurrency() var store = db.CreateStore(TenantA); var profile = CreateProfile(TenantA); - await store.AddAsync(profile); + await store.AddAsync(profile, ct: TestContext.Current.CancellationToken); profile.UpdateName( "First", @@ -302,7 +302,7 @@ public async Task SaveAsync_WhenVersionIsStale_ThrowsConcurrency() "First Update", Now.AddMinutes(1)); - await store.SaveAsync(profile, 0); + await store.SaveAsync(profile, 0, ct: TestContext.Current.CancellationToken); profile.UpdateName( "Second", @@ -329,7 +329,7 @@ public async Task DeleteAsync_Soft_PreservesProfileAndDeletionState() var store = db.CreateStore(TenantA); var profile = CreateProfile(TenantA); - await store.AddAsync(profile); + await store.AddAsync(profile, ct: TestContext.Current.CancellationToken); var deletedAt = Now.AddHours(1); @@ -340,13 +340,13 @@ await store.DeleteAsync( profile.ProfileKey), expectedVersion: 0, DeleteMode.Soft, - deletedAt); + deletedAt, ct: TestContext.Current.CancellationToken); var persisted = await store.GetAsync( new UserProfileKey( TenantA, profile.UserKey, - profile.ProfileKey)); + profile.ProfileKey), ct: TestContext.Current.CancellationToken); persisted.Should().NotBeNull(); persisted!.IsDeleted.Should().BeTrue(); @@ -366,16 +366,16 @@ public async Task DeleteAsync_Hard_RemovesProfile() profile.UserKey, profile.ProfileKey); - await store.AddAsync(profile); + await store.AddAsync(profile, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( key, 0, DeleteMode.Hard, - Now); + Now, ct: TestContext.Current.CancellationToken); - (await store.GetAsync(key)).Should().BeNull(); - (await store.ExistsAsync(key)).Should().BeFalse(); + (await store.GetAsync(key, ct: TestContext.Current.CancellationToken)).Should().BeNull(); + (await store.ExistsAsync(key, ct: TestContext.Current.CancellationToken)).Should().BeFalse(); } [Fact] @@ -409,7 +409,7 @@ public async Task DeleteAsync_WhenVersionIsStale_ThrowsConcurrency() profile.UserKey, profile.ProfileKey); - await store.AddAsync(profile); + await store.AddAsync(profile, ct: TestContext.Current.CancellationToken); profile.UpdateName( "Changed", @@ -417,7 +417,7 @@ public async Task DeleteAsync_WhenVersionIsStale_ThrowsConcurrency() null, Now.AddMinutes(1)); - await store.SaveAsync(profile, 0); + await store.SaveAsync(profile, 0, ct: TestContext.Current.CancellationToken); var act = () => store.DeleteAsync( key, @@ -456,7 +456,7 @@ public async Task SaveAsync_WhenEntityBelongsToDifferentTenant_IsRejected() var storeB = db.CreateStore(TenantB); var profile = CreateProfile(TenantB); - await storeB.AddAsync(profile); + await storeB.AddAsync(profile, ct: TestContext.Current.CancellationToken); var act = () => storeA.SaveAsync( profile, @@ -474,14 +474,14 @@ public async Task QueryAsync_IsTenantIsolated() var storeA = db.CreateStore(TenantA); var storeB = db.CreateStore(TenantB); - await storeA.AddAsync(CreateProfile(TenantA)); - await storeB.AddAsync(CreateProfile(TenantB)); + await storeA.AddAsync(CreateProfile(TenantA), ct: TestContext.Current.CancellationToken); + await storeB.AddAsync(CreateProfile(TenantB), ct: TestContext.Current.CancellationToken); var resultA = await storeA.QueryAsync( - new UserProfileQuery()); + new UserProfileQuery(), ct: TestContext.Current.CancellationToken); var resultB = await storeB.QueryAsync( - new UserProfileQuery()); + new UserProfileQuery(), ct: TestContext.Current.CancellationToken); resultA.Items.Should().ContainSingle(); resultA.Items.Single().Tenant.Should().Be(TenantA); @@ -514,8 +514,8 @@ public async Task GetAllProfilesByUserAsync_ReturnsActiveProfilesOnly() user, secondaryKey); - await store.AddAsync(first); - await store.AddAsync(second); + await store.AddAsync(first, ct: TestContext.Current.CancellationToken); + await store.AddAsync(second, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new UserProfileKey( @@ -524,10 +524,10 @@ await store.DeleteAsync( secondaryKey), 0, DeleteMode.Soft, - Now.AddHours(1)); + Now.AddHours(1), ct: TestContext.Current.CancellationToken); var result = - await store.GetAllProfilesByUserAsync(user); + await store.GetAllProfilesByUserAsync(user, ct: TestContext.Current.CancellationToken); result.Should().ContainSingle(); result.Single().ProfileKey.Should().Be(ProfileKey.Default); @@ -542,11 +542,11 @@ public async Task GetAllProfilesByUserAsync_DoesNotReturnOtherUsersProfiles() var userA = UserKey.New(); var userB = UserKey.New(); - await store.AddAsync(CreateProfile(TenantA, userA)); - await store.AddAsync(CreateProfile(TenantA, userB)); + await store.AddAsync(CreateProfile(TenantA, userA), ct: TestContext.Current.CancellationToken); + await store.AddAsync(CreateProfile(TenantA, userB), ct: TestContext.Current.CancellationToken); var result = - await store.GetAllProfilesByUserAsync(userA); + await store.GetAllProfilesByUserAsync(userA, ct: TestContext.Current.CancellationToken); result.Should().ContainSingle(); result.Single().UserKey.Should().Be(userA); @@ -562,13 +562,13 @@ public async Task GetByUsersAsync_ReturnsRequestedUsersForProfileKey() var userB = UserKey.New(); var userC = UserKey.New(); - await store.AddAsync(CreateProfile(TenantA, userA)); - await store.AddAsync(CreateProfile(TenantA, userB)); - await store.AddAsync(CreateProfile(TenantA, userC)); + await store.AddAsync(CreateProfile(TenantA, userA), ct: TestContext.Current.CancellationToken); + await store.AddAsync(CreateProfile(TenantA, userB), ct: TestContext.Current.CancellationToken); + await store.AddAsync(CreateProfile(TenantA, userC), ct: TestContext.Current.CancellationToken); var result = await store.GetByUsersAsync( [userA, userB], - ProfileKey.Default); + ProfileKey.Default, ct: TestContext.Current.CancellationToken); result.Should().HaveCount(2); @@ -589,8 +589,8 @@ public async Task GetByUsersAsync_ExcludesDeletedProfiles() var first = CreateProfile(TenantA, userA); var second = CreateProfile(TenantA, userB); - await store.AddAsync(first); - await store.AddAsync(second); + await store.AddAsync(first, ct: TestContext.Current.CancellationToken); + await store.AddAsync(second, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new UserProfileKey( @@ -599,11 +599,11 @@ await store.DeleteAsync( ProfileKey.Default), 0, DeleteMode.Soft, - Now.AddHours(1)); + Now.AddHours(1), ct: TestContext.Current.CancellationToken); var result = await store.GetByUsersAsync( [userA, userB], - ProfileKey.Default); + ProfileKey.Default, ct: TestContext.Current.CancellationToken); result.Should().ContainSingle(); result.Single().UserKey.Should().Be(userA); @@ -622,8 +622,8 @@ public async Task QueryAsync_ByDefault_ExcludesDeleted() var active = CreateProfile(TenantA); var deleted = CreateProfile(TenantA); - await store.AddAsync(active); - await store.AddAsync(deleted); + await store.AddAsync(active, ct: TestContext.Current.CancellationToken); + await store.AddAsync(deleted, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new UserProfileKey( @@ -632,10 +632,10 @@ await store.DeleteAsync( deleted.ProfileKey), 0, DeleteMode.Soft, - Now.AddHours(1)); + Now.AddHours(1), ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync( - new UserProfileQuery()); + new UserProfileQuery(), ct: TestContext.Current.CancellationToken); result.Items.Should().ContainSingle(); result.Items.Single().Id.Should().Be(active.Id); @@ -651,8 +651,8 @@ public async Task QueryAsync_IncludeDeleted_ReturnsDeletedProfiles() var active = CreateProfile(TenantA); var deleted = CreateProfile(TenantA); - await store.AddAsync(active); - await store.AddAsync(deleted); + await store.AddAsync(active, ct: TestContext.Current.CancellationToken); + await store.AddAsync(deleted, ct: TestContext.Current.CancellationToken); await store.DeleteAsync( new UserProfileKey( @@ -661,13 +661,13 @@ await store.DeleteAsync( deleted.ProfileKey), 0, DeleteMode.Soft, - Now.AddHours(1)); + Now.AddHours(1), ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync( new UserProfileQuery { IncludeDeleted = true - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Should().HaveCount(2); result.TotalCount.Should().Be(2); @@ -686,19 +686,19 @@ public async Task QueryAsync_ProfileKey_FiltersProfiles() await store.AddAsync( CreateProfile( TenantA, - profileKey: ProfileKey.Default)); + profileKey: ProfileKey.Default), ct: TestContext.Current.CancellationToken); var work = CreateProfile( TenantA, profileKey: workKey); - await store.AddAsync(work); + await store.AddAsync(work, ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync( new UserProfileQuery { ProfileKey = workKey - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Should().ContainSingle(); result.Items.Single().Id.Should().Be(work.Id); @@ -716,7 +716,7 @@ await store.AddAsync( CreateProfile( TenantA, createdAt: Now.AddMinutes(i), - displayName: $"User {i}")); + displayName: $"User {i}"), ct: TestContext.Current.CancellationToken); } var result = await store.QueryAsync( @@ -725,7 +725,7 @@ await store.AddAsync( PageNumber = 2, PageSize = 2, SortBy = nameof(UserProfile.CreatedAt) - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Should().HaveCount(2); result.TotalCount.Should().Be(5); @@ -754,27 +754,27 @@ await store.AddAsync( TenantA, firstName: "Charlie", lastName: "Zulu", - displayName: "Mike")); + displayName: "Mike"), ct: TestContext.Current.CancellationToken); await store.AddAsync( CreateProfile( TenantA, firstName: "Alice", lastName: "Alpha", - displayName: "Alpha")); + displayName: "Alpha"), ct: TestContext.Current.CancellationToken); await store.AddAsync( CreateProfile( TenantA, firstName: "Bob", lastName: "Mike", - displayName: "Zulu")); + displayName: "Zulu"), ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync( new UserProfileQuery { SortBy = sortBy - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Should().HaveCount(3); @@ -809,24 +809,24 @@ public async Task QueryAsync_SortsCreatedAtDescending() await store.AddAsync( CreateProfile( TenantA, - createdAt: Now.AddMinutes(1))); + createdAt: Now.AddMinutes(1)), ct: TestContext.Current.CancellationToken); await store.AddAsync( CreateProfile( TenantA, - createdAt: Now.AddMinutes(3))); + createdAt: Now.AddMinutes(3)), ct: TestContext.Current.CancellationToken); await store.AddAsync( CreateProfile( TenantA, - createdAt: Now.AddMinutes(2))); + createdAt: Now.AddMinutes(2)), ct: TestContext.Current.CancellationToken); var result = await store.QueryAsync( new UserProfileQuery { SortBy = nameof(UserProfile.CreatedAt), Descending = true - }); + }, ct: TestContext.Current.CancellationToken); result.Items.Select(x => x.CreatedAt) .Should() diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserApplicationServiceTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserApplicationServiceTests.cs index d4675dc9..7be1eed9 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserApplicationServiceTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserApplicationServiceTests.cs @@ -103,7 +103,7 @@ public async Task CreateUserAsync_WhenValid_CreatesLifecycleAndDefaultProfile() var result = await f.Sut.CreateUserAsync( context, - request); + request, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); @@ -156,7 +156,7 @@ public async Task CreateUserAsync_CreatesConfiguredIdentifiersForSameUser() var result = await f.Sut.CreateUserAsync( context, - request); + request, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); @@ -201,7 +201,7 @@ public async Task CreateUserAsync_InvokesLifecycleIntegrations() It.IsAny())) .Returns(Task.CompletedTask); - await f.Sut.CreateUserAsync(context, request); + await f.Sut.CreateUserAsync(context, request, ct: TestContext.Current.CancellationToken); integration.Verify(x => x.OnUserCreatedAsync( context.ResourceTenant, @@ -286,7 +286,7 @@ public async Task ChangeUserStatusAsync_Self_AllowsActiveToSelfSuspended() await f.Sut.ChangeUserStatusAsync( context, - request); + request, ct: TestContext.Current.CancellationToken); lifecycle.Status.Should().Be(UserStatus.SelfSuspended); @@ -429,7 +429,7 @@ public async Task DeleteMeAsync_SoftDeletesUserData_AndRevokesAllChains() It.IsAny())) .Returns(Task.CompletedTask); - await f.Sut.DeleteMeAsync(context); + await f.Sut.DeleteMeAsync(context, ct: TestContext.Current.CancellationToken); f.LifecycleStore.Verify(x => x.DeleteAsync( It.IsAny(), @@ -496,7 +496,7 @@ public async Task DeleteMeAsync_InvokesIntegrationWithSoftDelete() It.IsAny())) .Returns(Task.CompletedTask); - await f.Sut.DeleteMeAsync(context); + await f.Sut.DeleteMeAsync(context, ct: TestContext.Current.CancellationToken); integration.Verify(x => x.OnUserDeletedAsync( context.ResourceTenant, @@ -574,7 +574,7 @@ public async Task DeleteUserAsync_UsesRequestedDeleteMode( await f.Sut.DeleteUserAsync( context, - request); + request, ct: TestContext.Current.CancellationToken); f.LifecycleStore.Verify(x => x.DeleteAsync( It.IsAny(), @@ -1025,7 +1025,7 @@ await f.Sut.VerifyUserIdentifierAsync( { Id = identifier.Id, Proof = proof - }); + }, ct: TestContext.Current.CancellationToken); identifier.IsVerified.Should().BeTrue(); identifier.VerifiedAt.Should().Be(Now); @@ -1189,7 +1189,7 @@ await f.Sut.DeleteUserIdentifierAsync( { Id = target.Id, Mode = DeleteMode.Soft - }); + }, ct: TestContext.Current.CancellationToken); target.IsDeleted.Should().BeTrue(); target.DeletedAt.Should().Be(Now); @@ -1257,7 +1257,7 @@ await f.Sut.DeleteUserIdentifierAsync( { Id = target.Id, Mode = DeleteMode.Hard - }); + }, ct: TestContext.Current.CancellationToken); f.IdentifierStore.Verify(x => x.DeleteAsync( target.Id, @@ -1356,7 +1356,7 @@ await f.Sut.UpdateUserIdentifierAsync( { Id = identifier.Id, NewValue = "new@example.com" - }); + }, ct: TestContext.Current.CancellationToken); identifier.Value.Should().Be("new@example.com"); identifier.NormalizedValue.Should().Be("new@example.com"); @@ -1405,7 +1405,7 @@ await f.Sut.SetPrimaryUserIdentifierAsync( new SetPrimaryUserIdentifierRequest { Id = identifier.Id - }); + }, ct: TestContext.Current.CancellationToken); identifier.IsPrimary.Should().BeTrue(); identifier.UpdatedAt.Should().Be(Now); @@ -1500,7 +1500,7 @@ await f.Sut.UnsetPrimaryUserIdentifierAsync( new UnsetPrimaryUserIdentifierRequest { Id = target.Id - }); + }, ct: TestContext.Current.CancellationToken); target.IsPrimary.Should().BeFalse(); target.UpdatedAt.Should().Be(Now); @@ -1573,7 +1573,7 @@ await f.Sut.DeleteUserAsync( new DeleteUserRequest { Mode = mode - }); + }, ct: TestContext.Current.CancellationToken); f.SessionStore.Verify(x => x.RevokeAllChainsAsync( target, @@ -1672,7 +1672,7 @@ await f.Sut.DeleteUserAsync( new DeleteUserRequest { Mode = mode - }); + }, ct: TestContext.Current.CancellationToken); f.ProfileStore.Verify(x => x.DeleteAsync( new UserProfileKey( @@ -1719,7 +1719,7 @@ public async Task GetIdentifierAsync_WhenValueInvalid_ReturnsNullWithoutStoreAcc "invalid", "", false, "identifier_invalid")); var result = await f.Sut.GetIdentifierAsync( - context, UserIdentifierType.Email, "invalid"); + context, UserIdentifierType.Email, "invalid", ct: TestContext.Current.CancellationToken); result.Should().BeNull(); @@ -1751,7 +1751,7 @@ public async Task GetIdentifierAsync_WhenNotFound_ReturnsNull() .ReturnsAsync((UserIdentifier?)null); var result = await f.Sut.GetIdentifierAsync( - context, UserIdentifierType.Email, "ALICE@EXAMPLE.COM"); + context, UserIdentifierType.Email, "ALICE@EXAMPLE.COM", ct: TestContext.Current.CancellationToken); result.Should().BeNull(); @@ -1787,7 +1787,7 @@ public async Task GetIdentifierAsync_WhenFound_ReturnsMappedIdentifier() .ReturnsAsync(identifier); var result = await f.Sut.GetIdentifierAsync( - context, UserIdentifierType.Email, "alice@example.com"); + context, UserIdentifierType.Email, "alice@example.com", ct: TestContext.Current.CancellationToken); result.Should().NotBeNull(); result!.Id.Should().Be(identifier.Id); @@ -1808,7 +1808,7 @@ public async Task UserIdentifierExistsAsync_WhenValueInvalid_ReturnsFalseWithout "invalid", "", false, "identifier_invalid")); var result = await f.Sut.UserIdentifierExistsAsync( - context, UserIdentifierType.Email, "invalid"); + context, UserIdentifierType.Email, "invalid", ct: TestContext.Current.CancellationToken); result.Should().BeFalse(); @@ -1848,7 +1848,7 @@ public async Task UserIdentifierExistsAsync_TenantScope_DoesNotPassUserKey( context, UserIdentifierType.Email, "ALICE@EXAMPLE.COM", - scope); + scope, ct: TestContext.Current.CancellationToken); result.Should().BeTrue(); @@ -1884,7 +1884,7 @@ public async Task UserIdentifierExistsAsync_WithinUser_PassesTargetUserKey() context, UserIdentifierType.Username, "Alice", - IdentifierExistenceScope.WithinUser); + IdentifierExistenceScope.WithinUser, ct: TestContext.Current.CancellationToken); result.Should().BeTrue(); } @@ -1912,7 +1912,7 @@ public async Task UserIdentifierExistsAsync_WhenStoreReportsMissing_ReturnsFalse .ReturnsAsync(new UserIdentifierExistenceResult(Exists: false)); var result = await f.Sut.UserIdentifierExistsAsync( - context, UserIdentifierType.Username, "missing"); + context, UserIdentifierType.Username, "missing", ct: TestContext.Current.CancellationToken); result.Should().BeFalse(); } @@ -1948,7 +1948,7 @@ public async Task GetUsersByKeysAsync_WhenEmpty_ReturnsEmptyWithoutStoreAccess() var result = await f.Sut.GetUsersByKeysAsync( context, - Array.Empty()); + Array.Empty(), ct: TestContext.Current.CancellationToken); result.Should().BeEmpty(); @@ -1996,7 +1996,7 @@ public async Task GetUsersByKeysAsync_ShouldNormalizeKeysAndUseResourceTenant() var result = await f.Sut.GetUsersByKeysAsync( context, - [first, first, second]); + [first, first, second], ct: TestContext.Current.CancellationToken); result.Should().BeEquivalentTo(expected); @@ -2033,7 +2033,7 @@ public async Task GetUsersByKeysAsync_When250DistinctKeys_ExecutesSuccessfully() var result = await f.Sut.GetUsersByKeysAsync( context, - keys); + keys, ct: TestContext.Current.CancellationToken); result.Should().BeEmpty(); @@ -2162,7 +2162,7 @@ public async Task CreateUserAsync_WhenIdentifierCountEqualsLimit_Succeeds() SetupValidCreate(f, context, request); SetupSuccessfulCreatePersistence(f); - var result = await f.Sut.CreateUserAsync(context, request); + var result = await f.Sut.CreateUserAsync(context, request, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); @@ -2201,7 +2201,7 @@ public async Task CreateUserAsync_WithDefaultLimit_AllowsThreeInitialIdentifiers SetupValidCreate(f, context, request); SetupSuccessfulCreatePersistence(f); - var result = await f.Sut.CreateUserAsync(context, request); + var result = await f.Sut.CreateUserAsync(context, request, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeTrue(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserCreateValidatorTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserCreateValidatorTests.cs index 64aad5cd..307482f8 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserCreateValidatorTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserCreateValidatorTests.cs @@ -25,7 +25,7 @@ public async Task ValidateAsync_WhenNoIdentifierProvided_ShouldReturnIdentifierR var result = await fixture.Sut.ValidateAsync( fixture.Context, - request); + request, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); @@ -72,7 +72,7 @@ public async Task ValidateAsync_WhenIdentifierIsInvalid_ShouldNotCheckAvailabili var result = await fixture.Sut.ValidateAsync( fixture.Context, - CreateRequest(type, "invalid-value")); + CreateRequest(type, "invalid-value"), ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); @@ -121,7 +121,7 @@ public async Task ValidateAsync_WhenIdentifierIsUnavailable_ShouldReturnExpected var result = await fixture.Sut.ValidateAsync( fixture.Context, - CreateRequest(type, "value")); + CreateRequest(type, "value"), ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); @@ -155,7 +155,7 @@ public async Task ValidateAsync_WhenIdentifierIsValidAndAvailable_ShouldSucceed( var result = await fixture.Sut.ValidateAsync( fixture.Context, - CreateRequest(type, "value")); + CreateRequest(type, "value"), ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeTrue(); result.Errors.Should().BeEmpty(); @@ -190,7 +190,7 @@ public async Task ValidateAsync_WhenAllIdentifiersProvided_ShouldValidateAllIden UserName = "john", Email = "john@example.com", Phone = "1234567890" - }); + }, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeTrue(); @@ -238,7 +238,7 @@ await fixture.Sut.ValidateAsync( UserName = "john", Email = "john@example.com", Phone = "1234567890" - }); + }, ct: TestContext.Current.CancellationToken); fixture.IdentifierValidator.Verify( x => x.ValidateAsync( @@ -339,7 +339,7 @@ public async Task ValidateAsync_WhenOneIdentifierIsInvalid_ShouldContinueValidat { UserName = "invalid", Email = "john@example.com" - }); + }, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserIdentifierApplicationServiceTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserIdentifierApplicationServiceTests.cs index 4f31ff61..8cf080e5 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserIdentifierApplicationServiceTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserIdentifierApplicationServiceTests.cs @@ -27,9 +27,9 @@ await service.AddUserIdentifierAsync(context, Type = UserIdentifierType.Email, Value = "new@example.com", IsPrimary = true - }); + }, ct: TestContext.Current.CancellationToken); - var identifiers = await service.GetIdentifiersByUserAsync(context, new UserIdentifierQuery()); + var identifiers = await service.GetIdentifiersByUserAsync(context, new UserIdentifierQuery(), ct: TestContext.Current.CancellationToken); identifiers.Items.Where(x => x.Type == UserIdentifierType.Email).Should().ContainSingle(x => x.IsPrimary); @@ -61,14 +61,14 @@ await identifierService.AddUserIdentifierAsync(context, Type = UserIdentifierType.Phone, Value = "+905551111111", IsPrimary = true - }); + }, ct: TestContext.Current.CancellationToken); var result = await loginOrchestrator.LoginAsync(flow, new LoginRequest { Identifier = "+905551111111", Secret = "user", - }); + }, ct: TestContext.Current.CancellationToken); result.IsSuccess.Should().BeFalse(); } @@ -81,7 +81,7 @@ public async Task Adding_non_primary_should_not_affect_existing_primary() var context = TestAccessContext.ForUser(TestUsers.User, UserIdentifiers.AddSelf); - var before = await service.GetIdentifiersByUserAsync(context, new UserIdentifierQuery()); + var before = await service.GetIdentifiersByUserAsync(context, new UserIdentifierQuery(), ct: TestContext.Current.CancellationToken); var existingPrimaryEmail = before.Items.Single(x => x.Type == UserIdentifierType.Email && x.IsPrimary); await service.AddUserIdentifierAsync(context, @@ -90,9 +90,9 @@ await service.AddUserIdentifierAsync(context, Type = UserIdentifierType.Email, Value = "secondary@example.com", IsPrimary = false - }); + }, ct: TestContext.Current.CancellationToken); - var after = await service.GetIdentifiersByUserAsync(context, new UserIdentifierQuery()); + var after = await service.GetIdentifiersByUserAsync(context, new UserIdentifierQuery(), ct: TestContext.Current.CancellationToken); after.Items.Single(x => x.Type == UserIdentifierType.Email && x.IsPrimary) .Id.Should().Be(existingPrimaryEmail.Id); @@ -106,16 +106,16 @@ public async Task Updating_value_should_reset_verification() var context = TestAccessContext.ForUser(TestUsers.User, UserIdentifiers.UpdateSelf); - var email = (await service.GetIdentifiersByUserAsync(context, new UserIdentifierQuery())).Items.Single(x => x.Type == UserIdentifierType.Email); + var email = (await service.GetIdentifiersByUserAsync(context, new UserIdentifierQuery(), ct: TestContext.Current.CancellationToken)).Items.Single(x => x.Type == UserIdentifierType.Email); await service.UpdateUserIdentifierAsync(context, new UpdateUserIdentifierRequest { Id = email.Id, NewValue = "updated@example.com" - }); + }, ct: TestContext.Current.CancellationToken); - var updated = (await service.GetIdentifiersByUserAsync(context, new UserIdentifierQuery())).Items + var updated = (await service.GetIdentifiersByUserAsync(context, new UserIdentifierQuery(), ct: TestContext.Current.CancellationToken)).Items .Single(x => x.Id == email.Id); updated.IsVerified.Should().BeFalse(); @@ -146,7 +146,7 @@ await service.AddUserIdentifierAsync( Type = UserIdentifierType.Email, Value = "shared@example.com", IsPrimary = false - }); + }, ct: TestContext.Current.CancellationToken); Func act = () => service.AddUserIdentifierAsync( @@ -185,7 +185,7 @@ await service.AddUserIdentifierAsync( { Type = UserIdentifierType.Email, Value = "unique@example.com" - }); + }, ct: TestContext.Current.CancellationToken); Func act = () => service.AddUserIdentifierAsync( @@ -225,7 +225,7 @@ await service.AddUserIdentifierAsync( { Type = UserIdentifierType.Email, Value = "shared@example.com" - }); + }, ct: TestContext.Current.CancellationToken); Func act = () => service.AddUserIdentifierAsync( @@ -254,7 +254,7 @@ public async Task Unsetting_last_login_identifier_should_fail() var context = TestAccessContext.ForUser(TestUsers.User, UserIdentifiers.UnsetPrimarySelf); - var email = (await service.GetIdentifiersByUserAsync(context, new UserIdentifierQuery())).Items.Single(x => x.Type == UserIdentifierType.Email && x.IsPrimary); + var email = (await service.GetIdentifiersByUserAsync(context, new UserIdentifierQuery(), ct: TestContext.Current.CancellationToken)).Items.Single(x => x.Type == UserIdentifierType.Email && x.IsPrimary); Func act = async () => await service.UnsetPrimaryUserIdentifierAsync(context, @@ -279,7 +279,7 @@ await service.AddUserIdentifierAsync(context, { Type = UserIdentifierType.Email, Value = "Test@Example.com" - }); + }, ct: TestContext.Current.CancellationToken); Func act = async () => await service.AddUserIdentifierAsync(context, @@ -309,9 +309,9 @@ await service.AddUserIdentifierAsync(context, { Type = UserIdentifierType.Username, Value = "UserName" - }); + }, ct: TestContext.Current.CancellationToken); - var identifiers = await service.GetIdentifiersByUserAsync(context, new UserIdentifierQuery()); + var identifiers = await service.GetIdentifiersByUserAsync(context, new UserIdentifierQuery(), ct: TestContext.Current.CancellationToken); identifiers.Items.Should().Contain(x => x.Value == "UserName"); } @@ -333,7 +333,7 @@ await service.AddUserIdentifierAsync(context, { Type = UserIdentifierType.Username, Value = "UserName" - }); + }, ct: TestContext.Current.CancellationToken); Func act = async () => await service.AddUserIdentifierAsync(context, @@ -359,9 +359,9 @@ await service.AddUserIdentifierAsync(context, { Type = UserIdentifierType.Phone, Value = "+90 (555) 123-45-67" - }); + }, ct: TestContext.Current.CancellationToken); - var identifiers = await service.GetIdentifiersByUserAsync(context, new UserIdentifierQuery()); + var identifiers = await service.GetIdentifiersByUserAsync(context, new UserIdentifierQuery(), ct: TestContext.Current.CancellationToken); identifiers.Items.Should().Contain(x => x.Type == UserIdentifierType.Phone && @@ -381,16 +381,16 @@ await service.AddUserIdentifierAsync(context, { Type = UserIdentifierType.Email, Value = "one@example.com" - }); + }, ct: TestContext.Current.CancellationToken); await service.AddUserIdentifierAsync(context, new AddUserIdentifierRequest { Type = UserIdentifierType.Email, Value = "two@example.com" - }); + }, ct: TestContext.Current.CancellationToken); - var identifiers = await service.GetIdentifiersByUserAsync(context, new UserIdentifierQuery()); + var identifiers = await service.GetIdentifiersByUserAsync(context, new UserIdentifierQuery(), ct: TestContext.Current.CancellationToken); var second = identifiers.Items.Single(x => x.Value == "two@example.com"); Func act = async () => @@ -420,7 +420,7 @@ public async Task Availability_should_return_available_for_unused_identifier() { Type = UserIdentifierType.Username, Value = "unused-user-name" - }); + }, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeTrue(); result.IsAvailable.Should().BeTrue(); @@ -439,7 +439,7 @@ public async Task Availability_should_return_unavailable_for_existing_identifier var identifiers = await service.GetIdentifiersByUserAsync( context, - new UserIdentifierQuery()); + new UserIdentifierQuery(), ct: TestContext.Current.CancellationToken); var existing = identifiers.Items.First(); @@ -449,7 +449,7 @@ await service.GetIdentifiersByUserAsync( { Type = existing.Type, Value = existing.Value - }); + }, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeTrue(); result.IsAvailable.Should().BeFalse(); @@ -460,11 +460,11 @@ public async Task Availability_should_use_normalized_identifier() { var runtime = new TestAuthRuntime(); - var user = await runtime.CreateLoginUserAsync(); + var user = await runtime.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); const string storedEmail = "availability@example.com"; - await runtime.AddIdentifierAsync(user.UserKey, UserIdentifierType.Email, storedEmail); + await runtime.AddIdentifierAsync(user.UserKey, UserIdentifierType.Email, storedEmail, ct: TestContext.Current.CancellationToken); var service = runtime.GetUserApplicationService(); @@ -475,7 +475,7 @@ public async Task Availability_should_use_normalized_identifier() { Type = UserIdentifierType.Email, Value = " AVAILABILITY@EXAMPLE.COM " - }); + }, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeTrue(); result.IsAvailable.Should().BeFalse(); @@ -498,7 +498,7 @@ public async Task Availability_should_return_validation_errors_for_invalid_ident { Type = UserIdentifierType.Email, Value = "invalid" - }); + }, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.IsAvailable.Should().BeFalse(); @@ -511,12 +511,12 @@ public async Task Email_availability_should_be_case_insensitive_by_default() var runtime = new TestAuthRuntime(); var user = - await runtime.CreateLoginUserAsync(); + await runtime.CreateLoginUserAsync(ct: TestContext.Current.CancellationToken); await runtime.AddIdentifierAsync( user.UserKey, UserIdentifierType.Email, - "availability@example.com"); + "availability@example.com", ct: TestContext.Current.CancellationToken); var service = runtime.GetUserApplicationService(); @@ -532,7 +532,7 @@ await service.CheckIdentifierAvailabilityAsync( { Type = UserIdentifierType.Email, Value = "AVAILABILITY@EXAMPLE.COM" - }); + }, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeTrue(); result.IsAvailable.Should().BeFalse(); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserIdentifierValidationTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserIdentifierValidationTests.cs index 3b17c43d..5162951a 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserIdentifierValidationTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserIdentifierValidationTests.cs @@ -20,7 +20,7 @@ public async Task ValidateAsync_WhenIdentifierIsEmpty_ShouldReturnIdentifierEmpt { Type = UserIdentifierType.Username, Value = " " - }); + }, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.Errors.Should().ContainSingle(x => @@ -38,7 +38,7 @@ public async Task ValidateAsync_ShouldTrimIdentifierBeforeValidation() Value = " valid_user " }; - var result = await sut.ValidateAsync(null!, identifier); + var result = await sut.ValidateAsync(null!, identifier, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeTrue(); identifier.Value.Should().Be("valid_user"); @@ -58,7 +58,7 @@ public async Task ValidateAsync_WhenUsernameIsTooShort_ShouldReturnUsernameTooSh { Type = UserIdentifierType.Username, Value = "abc" - }); + }, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.Errors.Should().Contain(x => @@ -79,7 +79,7 @@ public async Task ValidateAsync_WhenUsernameIsTooLong_ShouldReturnUsernameTooLon { Type = UserIdentifierType.Username, Value = "abcdef" - }); + }, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.Errors.Should().Contain(x => @@ -101,7 +101,7 @@ public async Task ValidateAsync_WhenUsernameDoesNotMatchAllowedRegex_ShouldRetur { Type = UserIdentifierType.Username, Value = "user123" - }); + }, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.Errors.Should().Contain(x => @@ -123,7 +123,7 @@ public async Task ValidateAsync_WhenUsernameValidationIsDisabled_ShouldSkipUsern { Type = UserIdentifierType.Username, Value = "a" - }); + }, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeTrue(); } @@ -139,7 +139,7 @@ public async Task ValidateAsync_WhenEmailDoesNotContainAt_ShouldReturnInvalidFor { Type = UserIdentifierType.Email, Value = "not-an-email" - }); + }, ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeFalse(); result.Errors.Should().Contain(x => @@ -160,7 +160,7 @@ public async Task ValidateAsync_WhenEmailIsTooShort_ShouldReturnEmailTooShort() { Type = UserIdentifierType.Email, Value = "a@b.co" - }); + }, ct: TestContext.Current.CancellationToken); result.Errors.Should().Contain(x => x.Code == "email_too_short"); @@ -180,7 +180,7 @@ public async Task ValidateAsync_WhenEmailIsTooLong_ShouldReturnEmailTooLong() { Type = UserIdentifierType.Email, Value = "abc@example.com" - }); + }, ct: TestContext.Current.CancellationToken); result.Errors.Should().Contain(x => x.Code == "email_too_long"); @@ -200,7 +200,7 @@ public async Task ValidateAsync_WhenPhoneIsTooShort_ShouldReturnPhoneTooShort() { Type = UserIdentifierType.Phone, Value = "123" - }); + }, ct: TestContext.Current.CancellationToken); result.Errors.Should().Contain(x => x.Code == "phone_too_short"); @@ -220,7 +220,7 @@ public async Task ValidateAsync_WhenPhoneIsTooLong_ShouldReturnPhoneTooLong() { Type = UserIdentifierType.Phone, Value = "123456789" - }); + }, ct: TestContext.Current.CancellationToken); result.Errors.Should().Contain(x => x.Code == "phone_too_long"); diff --git a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserProfileValidatorTests.cs b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserProfileValidatorTests.cs index f54acbb6..0bd77a0f 100644 --- a/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserProfileValidatorTests.cs +++ b/tests/CodeBeam.UltimateAuth.Tests.Unit/Users/UserProfileValidatorTests.cs @@ -14,7 +14,7 @@ public async Task ValidateAsync_CurrentImplementation_ShouldSucceed() var result = await sut.ValidateAsync( null!, - new UserProfileInfo()); + new UserProfileInfo(), ct: TestContext.Current.CancellationToken); result.IsValid.Should().BeTrue(); result.Errors.Should().BeEmpty(); diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props new file mode 100644 index 00000000..b17f63d7 --- /dev/null +++ b/tests/Directory.Build.props @@ -0,0 +1,13 @@ + + + + $(UltimateAuthLatestTargetFramework) + + Exe + true + + + + + +