Skip to content

Fix API test authentication in BCApps - #10085

Open
Prangshuman Das (t-prda) wants to merge 153 commits into
mainfrom
prdas/646383-api-test-auth
Open

Fix API test authentication in BCApps#10085
Prangshuman Das (t-prda) wants to merge 153 commits into
mainfrom
prdas/646383-api-test-auth

Conversation

@t-prda

@t-prda Prangshuman Das (t-prda) commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Summary

Enable Business Central API tests under UserPassword authentication while retaining Windows/SaaS ambient authentication. HTTP-dependent tests run with Disabled isolation on freshly restored tenant workers.

AB#646383

Current head: b05ed58024. Includes the corrected three-line prerequisite from independent draft #11340, head 86497ab769; all eight native-stack follow-ups are refreshed. No exclusions, NAV selectors, authentication behavior, or other upstream files changed in this update. Fresh runtime CI and PowerShell CI are pending validation.

The first prerequisite attempt (a4ed3c3f18, auth 2028f76ec0) failed AL0175 because this built-in system option does not support direct = comparison. Those superseded runs were requested to stop after the corrected heads were published. The correction uses Assert.AreEqual(Format(expected), Format(actual), message). Read-only inspection of the local compiler's Format emitter confirms explicit Joker conversion through ALCompiler.ToNavValue; the runtime has a WebServiceActionResultCode overload. This is supporting implementation evidence, not a substitute for actual CI compilation and execution. The unchanged auth/scheduler code passed 69 local checks and PowerShell CI on 2028f76ec0.

The preceding initializer head 1916826fc7 reviewed all 160 Graph callers, moved license-safe date setup from OnRun into Initialize, and made provider selection once-only where an existing initialization guard and retained Graph instance permit it. Read-only review of 396c38..1916826 found no significant issues.

Run 34472110108, on 1916826fc7, finished failed at 2026-09-10T17:55:52Z: 68 failed workflow tasks, 94 successful and two skipped. All 67 failed test tasks were classified: 64 confirm CU 148339 CS1503 during discovery; three Uncategorized runners (CA, DE, GB) disconnected, with unavailable logs and confirming check annotations. The remaining failure is the aggregate status check. All 46 Legacy test tasks passed. W1 Legacy1 explicitly reports all six CU 139494 provider-contract methods successful on the initializer-refactored head. Credential removal succeeded on 110 test tasks; the three disconnected runners have no confirmed cleanup result.

Merged upstream main 2b77a80c1c and resolved the Expense exclusion-file conflict, preserving all 22 entries. GitHub reported this PR mergeable; no new BCApps-SyncMirror result was available at the last check. The discovery blocker came from upstream SpendRequestTest.Codeunit.al (CU 148339, introduced by #11007), whose three Assert.AreEqual calls require an unsupported built-in WebServiceActionResultCode to Variant conversion. Discovery compiles the codeunit before test filtering. Prerequisite #11340 compares the formatted actual and expected Updated values, preserving all three assertions and their messages without hardcoding localized text or numeric values. This separately reviewable prerequisite targets main independently; until it is merged there, its three lines are also present in this PR's diff. No PR is automatically merged.

Production head 6cd9bf1a09 passed all six AL provider-contract tests and PowerShell validation. Pull Request Build finished with 112/113 test jobs passing; the overall result is failed, solely due to 14 missing-General-Posting-Setup failures in unchanged Czech advance-payment tests (CUs148108/148109, CZ Default). They remain enabled and are not hidden by new exclusions. Credential cleanup succeeded in all113test jobs, including the failing lane. AL is unchanged from 2a553ac72f, whose Windows validation passed. Historical timing below is not a fresh measurement of this refactor.

Authentication design

  • An extensible API Test Authentication enum selects an API Test Auth Provider; None is the no-op default.
  • Tests explicitly select Microsoft Test Environment in Initialize, on the same codeunit-global Graph instance that sends requests. Selection is after existing early-return guards where the instance is retained (92 callers), inside four existing negative initialization branches, and idempotent in 64 unguarded initializers. Missing or unused initialization paths are reviewed and wired individually; selection is removed from OnRun. No new guard changes existing fixture/callback frequency.
  • Re-selecting the same provider is idempotent: it retains the interface instance and its cached state. AuthenticationProviderResolved remains instance-scoped for lazy default resolution.
  • The interface becomes ConfigureAuthentication(var Authentication: Codeunit "API Test Auth Context"). Providers configure credentials through this narrow context, not a mutable HTTP request.
  • No target-URL parameter or destination guard. Selecting a provider authorizes it to configure every request through that Graph instance, including overridden URLs.
  • The Microsoft provider exits first for SaaS and for Security Group.IsWindowsAuthentication(), the public System Application helper backed by the platform authentication check. It does not infer authentication from a User-table Windows SID or require a User-table read.
  • Otherwise, it reuses a successfully cached SecretText password; if uncached, it reads the protected container password when present, or Key Vault when absent. An unreadable/empty configured credential produces an error rather than silent fallback.
  • Password caching uses one SecretText; the extra resolved/required booleans and cache-result helper are removed.
  • Credential application still uses Http Web Request Mgt.AddBasicAuthentication. OnAfterInitializeWebRequestWithURL remains the final trusted request-customization event.

Partners can extend the enum and implement the provider without depending on Microsoft password-file or Key Vault conventions.

The supported trust boundary is admitted OnPrem test code plus protected secret provisioning, not Access = Internal. Existing public OnPrem Key Vault and file-backed test helpers already expose equivalent credential access in that environment. The wrapper adds no elevated AL permission declarations.

Credential setup records the exact backing path before copying the password. A final always() workflow step stops remaining container consumers and deletes that file before normal container teardown, including failure/cancellation paths where the runner remains available. Cleanup failures are surfaced; abrupt runner/VM loss remains outside the guarantee. The credential is not deleted after the first read or while parallel tests still need it.

Provider regression coverage includes the URL-free contract, no-auth default, final-event ordering, provider reuse, repeated selection, selection changes, and instance isolation. Structural checks cover the early Windows/SaaS exit, credential-source/cache ordering, and initialization entry points.

Test execution and scope

The HTTP request executes in a separate NST session and must see committed setup. Ordinary test transaction isolation cannot make that session part of the AL test transaction.

The scheduler discovers RequiredTestIsolation = Disabled from apps already selected for UnitTest, IntegrationTest and Uncategorized lanes, retaining test-type and disabled-test filters. Legacy buckets retain ordinary execution: the underlying helper ignores required isolation when no test type is supplied, so untyped clean discovery is deliberately not used. There is no separate app allowlist and no country-specific disabled-test logic.

It creates a database template, restores secondary tenants before clean codeunit execution, batches restores separately from execution, enables Task Scheduler for the clean phase, restores tenants afterward, and merges results into normal JUnit output. BCApps already had multitenant containers; this uses them as clean workers.

License-safe work dates remain: the helper is used by 117 test codeunits. Historical XML results show 46 distinct methods in 19 codeunits encountering the licensed-date restriction; 117 is the rollout scope, not the demonstrated failure count.

All 117 former OnRun date calls are removed. There are now 117 Initialize date sites, including six existing sites, plus 11 preserved method-local calls. Unlike provider state, WorkDate is session state: the runner reapplies company WorkDate before each method. Therefore the date is set before fixture creation and the initialization guard, not once per codeunit. Seventy-six previously indirect APIV2 test entries now call Initialize before creating fixtures.

The eight business-fixture/product areas below have been removed from this PR. The first seven account for 37 method-specific temporary exclusions across 20 codeunits (21 observed failures plus 16 additional affected methods). The extra APIV1 layer accounts for ten additional conservative exclusions; these are not newly observed failures of the reverted code. This makes 47 temporarily deferred methods, not entire disabled codeunits. Existing nine APIV2 whole-codeunit exclusions and all 22 newer/out-of-scope Expense Agent exclusions remain.

Native GitHub stack #11232

This PR #10085 is the bottom layer, targeting main. Eight follow-up PRs form a native GitHub stack above it, in the order below; each targets the preceding PR's branch and shows only its own fix and corresponding test re-enablement. GitHub's stack UI is available from these PRs. Existing PR numbers were retained without force-pushing any branch.

Area PR
VAT posting setup fixtures #11224
Credit-memo/invoice cancellation reason codes #11225
Italian payment-discount fixture/assertion #11226
Quote posting/document dates #11227
Vendor-payment journal numbering #11228
Additional field-comparison exclusions #11229
Production return-shipment PDF report selection #11230
APIV1 blank-line lookup, RapidStart polling/fixtures, journal handler and invoice Foundation setup #11322

Each fix layer removes only its own temporary exclusions (12, 10, 1, 2, 2, 10, 0, and 10 methods respectively). The top layer restores all 47 methods deferred by the auth base, while preserving pre-existing exclusions. Each child's parent tip is verified as an ancestor, and each comparison is restricted to that layer's intended files.

These are separately reviewable drafts, not claims of completed runtime validation. The repository's current pull-request build trigger targets main, releases/*, and features/*; later updates to layers targeting personal feature branches are not automatically covered by that trigger. No workflow-policy expansion or merge is performed here.

The additional APIV1 layer changes eight AL files plus its exclusion manifest. Its RapidStart fixture-lifetime fix removes that codeunit's guard, so provider selection there becomes per-invocation but stays idempotent; license-safe WorkDate still precedes fixture cleanup/generation. Existing Expense fixture changes are not part of this additional split.

Historical validation and NAV evidence

Prior-head refactor evidence (not validation of the latest initializer changes)

  • Run 34316231134, head 6cd9bf1a09: 160 jobs succeeded, two failed (CZ Default and the aggregate status check), two skipped. Of113test jobs,112passed; no other test lane failed.

  • Final W1 Legacy1 JUnit confirms CU139494 has six cases, zero failures, and zero skips: default no-auth, extended provider/event ordering, reuse, instance scoping, repeated selection, and switching to None.

  • W1 Legacy1 live log confirms ordinary Legacy execution; the erroneous938-codeunit clean-discovery loop is no longer used. All113explicit credential-cleanup steps succeeded.

  • The remaining CI blocker is14Czech purchase/sales advance-payment methods lacking General Posting Setup. Those source files are unchanged by this PR; no workaround or new exclusion was added.

  • NAV Buddybuild2726212 and SDL2726213 passed the identical AL implementation (before the final PowerShell/workflow-only review batch). Gatejob3634036 verifies211APIV1/APIV2Integrationmethods passed under Windows. NAV selection remains unchanged and the known legacy selection gap is not claimed fixed.

  • The refactor's second CI run compiled successfully and produced W1 Integration (2,840 tests) and Uncategorized (2,658 tests) results with zero failures. NAV Buddybuild 2726212 and SDL 2726213 passed; gatejob 3634036 reports 211 APIV1/APIV2 Integration methods passing under Windows.

  • That CI run was not green overall: CZ Default had 14 missing-General-Posting-Setup fixture failures in unchanged advance-payment tests, and US Uncategorized had a client-close timeout despite zero published JUnit assertion failures.

  • A real scheduler regression was then identified: untyped Legacy discovery scheduled 938 ordinary W1 codeunits as clean Disabled work, causing six-hour timeouts and preventing provider-contract results from being collected. 6cd9bf1a09 corrected this; the final run confirmed ordinary Legacy execution and all six provider cases passed. None of the earlier timeouts is described as successful validation.

  • Earlier BCApps head a6fac87c69: Pull Request Build and PowerShell checks passed; 173 checks passed and two skipped. Four provider tests and 50 scheduler/static tests passed at that stage. These are not results for the current refactor.

  • Pointer-only NAV PR #253796, using isolated BCApps cc8167987e: Buddybuild 2721741 and SDL 2721742 passed on merge 066b652da5b7d058e5fd21ac77089849b37381a7. No automatic merge is enabled. NAV selection, native exclusions and company setup were not changed.

  • ADO published no method-level TRX, but gatejob task logs do contain method results. Uptake job 3633108 reported no APIV1/APIV2 methods in W1 web-service execution; its typed Integration task ran APIV1 - Item Variants E2E.TestDeleteInUse successfully.

  • A normal NAV master baseline, job 3633605, reported 806 APIV1/APIV2 methods in W1 web-service execution and all 12 VAT-related methods passing across W1 web-service/Integration tasks. Item Variants TestDeleteInUse also passed in CA and US Integration.

  • The selection gap is separate: NAV's legacy caller uses a Disabled runner but defaults required-isolation selection to None; newly marked Disabled codeunits can be filtered out. It is reported, not fixed here.

  • Configured company names/types match for the seven VAT-related codeunits. NAV restores legacy Extended demo data; BCApps regenerates CRONUS with Contoso and resets clean tenants per codeunit. Matching names and passing baseline methods do not identify the precise causal VAT-data difference.

How much CI time does API enablement add?

The measured increase was about 13-15% in aggregate runner consumption, not a 13-15% increase in developer waiting time. Overall workflow elapsed increased by 2.44 minutes at the median and 5.73 minutes on average.

A paired experiment used fixed control #10904 and API-enabled #10905, with matching harmless CI markers. Run IDs: 33528153281 and 33528158102. Three complete pairs passed (attempts 1, 4 and 8); infrastructure-invalid attempts were excluded.

Metric Mean paired change Median Observed range
W1 aggregate runner time +74.46 min / +14.61% +15.20% +13.06% to +15.57%
All-country aggregate runner time +1,482.42 min / +12.75% +12.84% +11.09% to +14.32%
Longest W1 test job +4.30 min +4.19 min -0.95 to +9.67 min
Longest all-country test job +0.55 min -0.43 min -4.43 to +6.51 min
Overall workflow elapsed +5.73 min +2.44 min +1.37 to +13.38 min

Aggregate runner time sums parallel job durations; it is neither elapsed pipeline time nor measured CPU utilization. Longest-job duration is a critical-path proxy, not a full workflow dependency-path measurement.

There is no dedicated API job. Work is absorbed by existing Default, Integration and Uncategorized lanes. Their mean W1 job-duration increases were +27.66, +13.20 and +27.67 minutes respectively; Legacy lane changes were variable. Build startup and runner allocation also affect overall elapsed time.

Limitations: only three of five planned successful pairs were obtained; subsequent frozen runs hit warning-baseline drift. These measurements cover the earlier broader API-enabled implementation, before the current business-fix split and initialization/provider refactor. They are the available empirical estimate, not a guaranteed increase or a fresh measurement of the final refactored PR.

@t-prda
Prangshuman Das (t-prda) requested review from a team August 10, 2026 11:01
@github-actions github-actions Bot added Build: scripts & configs Build scripts and configuration files AL: Apps (W1) Add-on apps for W1 Team: Integrations GitHub request for Integrations area labels Aug 10, 2026
@github-actions github-actions Bot added this to the Version 29.0 milestone Aug 10, 2026
Comment thread build/scripts/NewBcContainer.ps1 Fixed
Comment thread build/scripts/NewBcContainer.ps1 Fixed
Comment thread src/Layers/W1/Tests/TestLibraries/LibraryGraphAuthMgt.Codeunit.al Outdated
Comment thread src/Layers/W1/Tests/TestLibraries/LibraryGraphAuthMgt.Codeunit.al Outdated
@t-prda
Prangshuman Das (t-prda) marked this pull request as draft August 10, 2026 11:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR centralizes Basic authentication injection for Library - Graph Mgt-based API tests so they can run in NavUserPassword (UserPassword) containers, and then re-enables several previously disabled API/E2E test suites. It replaces an app-specific Expense Agent auth helper with a shared TestLibraries subscriber and adds a container-side “credential bridge” file so the test runner can obtain the password without requiring Azure Key Vault.

Changes:

  • Added Library - Graph Auth Mgt. as an event subscriber to inject Basic auth for non-Windows test users, sourcing the password from a container file or (fallback) Azure Key Vault.
  • Removed the Expense Agent test-only auth helper + manual subscription binding; tests now rely on the shared subscriber.
  • Re-enabled multiple API/E2E test suites by removing entries from various *.DisabledTest.json files (and deleting the APIV1/APIV2 exclusion lists), and updated container provisioning to create the API-test password bridge file.

Reviewed changes

Copilot reviewed 14 out of 15 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/Layers/W1/Tests/TestLibraries/LibraryGraphAuthMgt.Codeunit.al New shared subscriber that injects Basic auth into Library - Graph Mgt requests for NavUserPassword scenarios.
build/scripts/NewBcContainer.ps1 Writes the container password bridge file and sets ACLs so server-side AL can read it.
src/Apps/W1/ExpenseAgent/test/src/Helper/ExpenseAPITestAuthHelper.Codeunit.al Removes app-specific auth injection helper in favor of shared TestLibraries implementation.
src/Apps/W1/ExpenseAgent/test/src/API/ExpenseUsersAPITest.Codeunit.al Drops manual subscription binding to the removed helper.
src/Apps/W1/ExpenseAgent/test/src/API/ExpenseProjectsAPITest.Codeunit.al Drops manual subscription binding to the removed helper.
src/Apps/W1/ExpenseAgent/test/src/API/ExpensePerDiemLocationsTest.Codeunit.al Drops manual subscription binding to the removed helper.
src/Apps/W1/ExpenseAgent/test/src/API/ExpenseCapabilitiesAPITest.Codeunit.al Drops manual subscription binding to the removed helper.
src/DisabledTests/Tests-Integration/Tests-Integration.DisabledTest.json Re-enables specific integration API tests by removing disable entries.
src/DisabledTests/Tests-Graph/Tests-Graph.DisabledTest.json Re-enables specific Graph E2E tests by removing disable entries.
src/DisabledTests/Sustainability_Tests/Sustainability_Tests.DisabledTest.json Re-enables Sustainability API tests by removing disable entries.
src/DisabledTests/Quality_Management-Tests/Quality_Management-Tests.DisabledTest.json Re-enables Quality Management API tests by removing disable entry.
src/DisabledTests/IRS_Forms_Tests/IRS_Forms_Tests.DisabledTest.json Re-enables IRS 1099 API test by removing disable entry.
src/DisabledTests/E-Document_Core_Tests/E-Document Core Tests.DisabledTest.json Re-enables E-Document API tests by removing disable entry.
src/DisabledTests/_Exclude_APIV2__Tests/_Exclude_APIV2__Tests.DisabledTest.json Deletes the APIV2 exclusion list (re-enables APIV2 suite).
src/DisabledTests/_Exclude_APIV1__Tests/_Exclude_APIV1__Tests.DisabledTest.json Deletes the APIV1 exclusion list (re-enables APIV1 suite).

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread build/scripts/NewBcContainer.ps1 Outdated
Comment thread build/scripts/NewBcContainer.ps1 Outdated
Comment thread src/Layers/W1/Tests/TestLibraries/LibraryGraphAuthMgt.Codeunit.al Outdated
Comment thread src/Layers/W1/Tests/TestLibraries/LibraryGraphAuthMgt.Codeunit.al Outdated
Comment thread src/Layers/W1/Tests/TestLibraries/LibraryGraphAuthMgt.Codeunit.al Outdated
Comment thread src/Layers/W1/Tests/TestLibraries/LibraryGraphAuthMgt.Codeunit.al Outdated
Comment thread src/Layers/W1/Tests/TestLibraries/LibraryGraphAuthMgt.Codeunit.al Outdated
Comment thread build/scripts/NewBcContainer.ps1 Outdated
Comment thread src/Layers/W1/Tests/TestLibraries/LibraryGraphAuthMgt.Codeunit.al Outdated
Comment thread src/Layers/W1/Tests/TestLibraries/LibraryGraphAuthMgt.Codeunit.al Outdated
@t-prda

Copy link
Copy Markdown
Contributor Author

Agentic PR Review - Round 1

Recommendation: Accept

What this PR does

This PR adds an explicit authentication bridge for API tests that use Library - Graph Mgt. in BCApps UserPassword containers. The subscriber is manual, each affected API test codeunit opts in, Windows-authenticated NAV gates keep their existing behavior, and local NAV UserPassword runs can use the existing Key Vault secret.

The change addresses the gate difference directly. It does not change application API behavior, and it avoids making partner test code automatically depend on the BCApps credential bridge.

Suggestions

None.

Risk assessment and necessity

Risk: The change touches shared test infrastructure and re-enables many existing suites, so CI isolation and concurrency failures may still need separate gate work. The authentication subscriber itself is manually scoped and internal.

Necessity: The change is required because BCApps runs these tests with UserPassword while NAV's normal uptake gates use Windows authentication. Without the bridge, the API suites fail with 401 responses and remain disabled.


[AI-PR-REVIEW] version=1 promptVersion=1 system=github pr=10085 round=1 by=t-prda at=2026-08-12T09:27:33Z lastSha=6fe7981474d813fcafab57bc6e6fa0c429df80a5 reviewKey=e0d301ad1af589367637a08b287af9af4fb26402df7532abc2876a0a48dbabc7 suggestions=none

@github-actions github-actions Bot added the Build: Automation Workflows and other setup in .github folder label Aug 13, 2026
Preserve ordinary Legacy execution because untyped discovery ignores required isolation. Add OS temp fallback and explicit final credential cleanup with 69 passing mocked regressions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3952f078-a881-4da8-ad96-13b727e48a91
@github-actions github-actions Bot added the Build: Automation Workflows and other setup in .github folder label Sep 9, 2026
@t-prda

Copy link
Copy Markdown
Contributor Author

Review follow-ups are implemented in 6cd9bf1 and propagated through native stack11232. Linux temp-path handling now falls back to System.IO.Path.GetTempPath at both cached-state locations, with a regression that unsets both environment variables. S5 credential lifetime now has an explicit final always() cleanup: setup records the backing path before copying; cleanup stops remaining container consumers before deleting the exact credential file, then normal container teardown still runs. Missing files/containers are handled; stop/deletion errors are surfaced, and hard runner/VM loss remains a limitation. No credential is removed after only the first provider read. The combined mocked/Pester suite passes69/69; current pipeline34316231134 is validating real execution. Separately, this batch corrects an observed Legacy discovery regression that mistakenly scheduled938ordinarycodeunits as clean Disabled work. NAV selection is unchanged. IN/VAT author threads and the Internal-access advisory have been replied to and resolved per the agreed disposition; production PDF remains only in #11230.

Comment thread src/Layers/W1/Tests/TestLibraries/LibraryGraphMgt.Codeunit.al
Comment thread build/scripts/tests/Remove-ApiTestPassword.Test.ps1 Outdated
@alexei-dobriansky

Copy link
Copy Markdown
Contributor

Good Sense Reviewer - Round 9

Recommendation: Accept

What this PR does

This PR adds provider-based authentication for API tests and runs HTTP-dependent Disabled-isolation API codeunits on clean tenant workers. The latest commit records and cleans up the shared credential file, uses a safe temp-directory fallback, and keeps Legacy buckets on ordinary execution.

I rechecked the current three-dot diff. Authentication is selected in each Graph test initialization before initialized guards, the Microsoft provider exits for SaaS and Windows authentication before reading credentials, and the clean scheduler now uses typed discovery only. The current cleanup step stops remaining container consumers and removes the exact host-backed ApiTestPassword file, so the open credential-lifetime blocker is addressed.

Status of previous suggestions
ID Title Status Author response
S1 Separate the production report change Addressed Withdrawn for this PR; the current net diff does not include that production report file.
S2 Cache the mock Key Vault setup Addressed Still addressed in the current provider implementation.
S3 Keep the affected-app test exact Addressed Still addressed in the current scheduler tests.
S4 Clarify the E-Doc Graph variables Addressed Still addressed in the current E-Document test changes.
S5 Clean up the container password file Addressed The latest commit records the backing path, runs an always() cleanup after build consumers, stops the matching container, and deletes the exact credential file.
New observations (commits since round 8)

None. The latest changes address the remaining credential cleanup concern, fix temp-directory fallback behavior, and avoid untyped Legacy clean discovery without introducing a new changed-span issue.

Risk assessment and necessity

Risk: The main risk is broad test-infrastructure behavior: API authentication, tenant resets, disabled-isolation scheduling, result merging, and credential lifetime. The PowerShell validation for this head passed, and the static checks I reran found no diff whitespace issue, no PowerShell parse error, no missing Graph authentication initialization, and no production report file in this PR. The full pull-request build was still running at review time.

Necessity: The change is needed so UserPassword API tests can authenticate while Windows and SaaS runs keep ambient authentication. The clean-tenant scheduler is also needed because these HTTP requests run in a separate server session and must see committed setup.


[AI-PR-REVIEW] version=1 promptVersion=4 system=github pr=10085 round=9 by=alexei-dobriansky at=2026-09-09T06:17:53Z lastSha=6cd9bf1a093f6c73d0acd6eca987be6cefb87045 reviewKey=9c7849360b1be065aee5606f116bad0fbf035a92b93115234867656b160743da suggestions=S1@b79ed4f7:addressed,S2@373f05d4:addressed,S3@daa56c26:addressed,S4@7d6c760a:addressed,S5@227b86e9:addressed parentRound=8

@t-prda

Copy link
Copy Markdown
Contributor Author

Final verification for auth head6cd9bf1a09: PowerShell checks and69localPsterregressions passed. Final W1Legacy1JUnit confirms all6provider-contract tests passed with0failures/0skips. The all-country run34316231134 finished with112/113test jobs passing; all113explicitcredential-cleanup steps succeeded. OverallCI remains failed because CZDefault repeats14missing-General-Posting-Setup failures in unchanged CZZadvance-payment tests; the aggregate statuscheck reflects that lane. No extra exclusions were added to hide them. The Legacy discovery regression is corrected and ordinaryLegacyexecution/provider-testexecution are verified. Windows validation2726212/2726213 passed the identical AL implementation, with211APIIntegrationmethods confirmed in gatejob3634036. Native stack11232 remains intact with #10085at its bottom and seven focused draft layers; nothing was merged or force-pushed. Both PR descriptions now contain final results and evidence limits.

Replace mocked Windows host drives with Pester TestDrive paths; production cleanup behavior is unchanged.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3952f078-a881-4da8-ad96-13b727e48a91
@alexei-dobriansky

Copy link
Copy Markdown
Contributor

Good Sense Reviewer - Round 10

Recommendation: Accept

What this PR does

Since round 9, the only new non-merge commit changes the credential cleanup tests to use paths built from the Pester test drive instead of hard-coded Windows paths. The changed span is present in the net PR diff and is limited to build/scripts/tests/Remove-ApiTestPassword.Test.ps1.

The new test fixture still checks the same cleanup contract: only the exact ApiTestPassword file is accepted, the backing mount path is recorded before the credential copy, and the expected workflow environment value is built from the same mocked host mount path. I found no new production-script or AL behavior change in this round.

Status of previous suggestions
ID Title Status Author response
S1 Separate the production report change Addressed The production report file remains outside the current net diff.
S2 Cache the mock Key Vault setup Addressed Still addressed in the current provider implementation.
S3 Keep the affected-app test exact Addressed Still addressed in the current scheduler tests.
S4 Clarify the E-Doc Graph variables Addressed Still addressed in the current E-Document test changes.
S5 Clean up the container password file Addressed The cleanup path remains registered, stopped, and deleted through the exact backing file flow.
New observations (commits since round 9)

None - the new commit only makes the credential cleanup tests platform-neutral.

Risk assessment and necessity

Risk: This round adds no new runtime path. The remaining regression surface is the larger API test authentication and clean-tenant scheduler work already reviewed in earlier rounds; the latest commit reduces platform-specific test risk by removing Windows-only mocked paths.

Necessity: The change is needed because the cleanup tests should validate the workflow path logic on non-Windows runners as well as Windows. The scope is narrow and matches that need.


[AI-PR-REVIEW] version=1 promptVersion=4 system=github pr=10085 round=10 by=alexei-dobriansky at=2026-09-09T18:21:15Z lastSha=65d56fb15680e0a2a86f366b8e7cacef5ab98470 reviewKey=344c5defaec39f2d997b191d1acc5905a7e7ab9a82c8406ae23e85cce8eae668 suggestions=S1@b79ed4f7:addressed,S2@373f05d4:addressed,S3@daa56c26:addressed,S4@7d6c760a:addressed,S5@227b86e9:addressed parentRound=9

Merge current main, retain12new Travel Request/capability exclusions and10existing deferred entries, and adapt the newly merged test's obsolete auth helper reference without enabling its tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3952f078-a881-4da8-ad96-13b727e48a91
/// <summary>
/// Specifies the authentication provider used for API test requests.
/// </summary>
enum 131023 "API Test Authentication" implements "API Test Auth Provider"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Interfaces}$

This new authentication seam is modelled as an extensible enum that implements the interface, so tests must extend the enum just to inject a mock provider. For an injectable dependency, prefer accepting Interface "API Test Auth Provider" directly and assign the implementing codeunit to the interface variable; keep enum-backed selection only as optional convenience wiring.

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.41.6

Caption = 'None';
Implementation = "API Test Auth Provider" = "No API Test Auth Provider";
}
value(1; "Microsoft Test Environment")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟠\ High\ Severity\ —\ Security}$

The new public authentication selector exposes privileged work that only looks protected by Access = Internal: any dependent test app can select Enum::"API Test Authentication"::"Microsoft Test Environment" and drive Library - Graph Mgt to attach the current environment's API-test password to a caller-chosen request. internal is not an authorization boundary; keep this provider behind a non-public selector or add an explicit authorization/host restriction before credentials are applied.

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.41.6

codeunit 139842 "APIV2 - Pictures E2E"
{
Subtype = Test;
RequiredTestIsolation = Disabled;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🔴\ Critical\ Severity\ —\ Testing}$

This PR starts opting the API E2E suites into RequiredTestIsolation = Disabled even though the tests commit setup data before calling the API (for example this codeunit commits customers, vendors, items, contacts, and pictures in nearly every test). That means the suite now runs under a non-isolating runner while leaving committed records behind for later tests, creating order-dependent results instead of a clean database per test/codeunit.

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.41.6

@alexei-dobriansky

Copy link
Copy Markdown
Contributor

Good Sense Reviewer - Round 11

Recommendation: Accept with Suggestions

What this PR does

This change adds shared API test authentication, container password provisioning and cleanup, and clean-tenant scheduling so API tests can run under UserPassword without breaking ambient-auth environments.

The approach fits the problem: the provider exits for ambient authentication, Basic auth is applied through the shared Graph test library when needed, and the broad API test opt-in matches the enablement scope. The remaining concerns are in the runner: the transient classifier is still too broad, and clean Disabled-isolation result files can collide with later tenant result files.

Status of previous suggestions
ID Title Status Author response
S1 Separate the production report change Addressed The production report file remains outside the current net diff.
S2 Cache the mock Key Vault setup Addressed Still addressed in the current provider implementation.
S3 Keep the affected-app test exact Addressed Still addressed in the current scheduler tests.
S4 Clarify the E-Doc Graph variables Addressed Still addressed in the current E-Document test changes.
S5 Clean up the container password file Addressed The cleanup path remains registered, stopped, and deleted through the exact backing file flow.
New observations (commits since round 10)

S6 (🟠 Moderate): Narrow the transient error match
The new transient detector matches any ClientSession State is InError, not only the known page-130455 race. That can re-run a real client or test failure as a transient platform issue. Please include the specific page 130455 or InteractionManager context in this match.

S7 (🟠 Moderate): Use separate clean-test result files
Clean Disabled-isolation codeunits write to the same tenant-suffixed result files that normal app jobs use later. A later normal job on that tenant can replace the clean-codeunit XML before the final merge. Please use a distinct suffix for clean-codeunit jobs and merge it explicitly.

Risk assessment and necessity

Risk: Product behavior is not changed, but this test-infrastructure path can hide failures or lose published test evidence. Credential handling now creates a shared file in the container mount; the ACL and cleanup flow reduce exposure, while abrupt runner loss remains outside the code guarantee.

Necessity: The API tests cannot be broadly re-enabled in UserPassword containers unless the HTTP request session can authenticate and Disabled-isolation tests have fresh tenant workers. The scope is appropriate for a test-enablement base, with business fixture fixes kept separate.


[AI-PR-REVIEW] version=1 promptVersion=4 system=github pr=10085 round=11 by=alexei-dobriansky at=2026-09-10T06:30:39Z lastSha=396c38c8c3a209011e68bfd082f7aef9bc2d2f23 reviewKey=53b451d962b2311d960e22f010ae640c1ef7048c019c4aa37eed85dede185c08 suggestions=S1@b79ed4f7:addressed,S2@373f05d4:addressed,S3@daa56c26:addressed,S4@7d6c760a:addressed,S5@227b86e9:addressed,S6@8ea4b299:new,S7@66cd1f4e:new parentRound=10

Follow reviewed global-instance lifetimes: provider after existing initialization guards, work date per test. Separate residual APIV1 reliability fixes and preserve deferred method scope for an additional native-stack layer.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3952f078-a881-4da8-ad96-13b727e48a91
Assert.IsTrue(LibraryGraphMgt.GetObjectIDFromJSON(ResponseText, 'id', ApprovalEntryId), 'Could not find approval entry');
end;

local procedure Initialize()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Testing}$

This PR's rollout re-enables broad API/E2E coverage, but 37 of the changed test codeunits call LibraryGraphMgt.SetAuthenticationProvider(Enum::"API Test Authentication"::"Microsoft Test Environment") in their Initialize procedure without also calling LibraryGraphMgt.SetLicenseSafeWorkDate(), unlike the other ~113+ peer suites updated in the same PR. That leaves these tests running on the ambient work date instead of the November-15 license-safe date the rest of the rollout now depends on, which is inconsistent and could reintroduce the license/date sensitivity this change set is otherwise fixing, especially for the tests being re-enabled via the large deletions in _Exclude_APIV1__Tests.DisabledTest.json, _Exclude_APIV2__Tests.DisabledTest.json, and Expense_Agent_Tests.DisabledTest.json. Recommend confirming this omission is intentional (e.g. those specific suites are date-insensitive) or adding the missing SetLicenseSafeWorkDate() call for consistency.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.41.6

VerifyNoRemainingCalls();
end;

local procedure Initialize()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Testing}$

API Test Auth Provider Tests (codeunit 139494) forces EnvironmentInfoTestLibrary.SetTestabilitySoftwareAsAService(false) inside its Initialize() procedure and never restores the prior environment setting afterward. Environment Info Test Library mocks a SingleInstance-like environment flag, so if this codeunit does not run with full test isolation, later tests executed in the same session can inherit the forced on-prem environment mock and exercise authentication logic (which branches on EnvironmentInfo.IsSaaSInfrastructure() in Microsoft Test Auth Provider) under the wrong environment contract, making suite behavior order-dependent. Recommend explicitly restoring the previous environment setting (or bracketing the mock per test) rather than relying on isolation defaults.

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.41.6

@alexei-dobriansky

Copy link
Copy Markdown
Contributor

Good Sense Reviewer - Round 12

Recommendation: Accept with Suggestions

What this PR does

This change adds shared authentication for API tests, prepares clean tenants for tests that require disabled isolation, and re-enables the affected API suites under UserPassword while preserving ambient-auth runs.

The latest commit moves license-safe dates into each test initialization path and keeps provider selection once-only where the Graph library instance is retained. That direction is sound: the static scan found all 160 Graph-requesting test codeunits still select the Microsoft test provider, none select it from OnRun, and the date setup is no longer left in OnRun. The two runner concerns from the prior review are still open.

Status of previous suggestions
ID Title Status Author response
S1 Separate the production report change Addressed The production report file remains outside the current net diff.
S2 Cache the mock Key Vault setup Addressed The provider still caches only a successfully resolved password and keeps the container credential ahead of the fallback source.
S3 Keep the affected-app test exact Addressed The structural test still checks the exact Graph-requesting codeunits and now covers 160 callers.
S4 Clarify the E-Doc Graph variables Addressed The current diff does not reintroduce the unclear Graph variable pattern.
S5 Clean up the container password file Addressed The setup still records the exact backing file and the final cleanup stops consumers before deleting that file.
S6 Narrow the transient error match Not addressed The classifier still treats a plain ClientSession State is InError as transient without the page 130455 or InteractionManager context.
S7 Use separate clean-test result files Not addressed Clean disabled-isolation codeunits still use tenant-suffixed result files that normal app jobs can reuse later.
New observations (commits since round 11)

None - the latest changes only move authentication/date setup into initialization paths and did not add a new changed-span issue.

Risk assessment and necessity

Risk: Product behavior is unchanged, but the test-infrastructure path is broad. The remaining risk is that the runner may retry a real failure as transient, or lose clean-codeunit result evidence when a later normal job writes the same tenant result file. Current runtime CI is still pending.

Necessity: The API suites need a shared authentication path and clean disabled-isolation execution to run reliably in UserPassword containers. The scope remains justified, and the latest initialization changes are necessary to keep test dates license-safe before fixtures are created.


[AI-PR-REVIEW] version=1 promptVersion=4 system=github pr=10085 round=12 by=alexei-dobriansky at=2026-09-10T12:31:33Z lastSha=1916826fc70638192a121738a18b6c2c3075a5d5 reviewKey=22a838a5888a96ec139cfbcb063d62adfe14c3aa07043620e7dc837d637587f9 suggestions=S1@b79ed4f7:addressed,S2@373f05d4:addressed,S3@daa56c26:addressed,S4@7d6c760a:addressed,S5@227b86e9:addressed,S6@8ea4b299:notaddressed,S7@66cd1f4e:notaddressed parentRound=11

Compare the built-in WebServiceActionResultCode directly and pass the Boolean to Assert.IsTrue instead of requiring an unsupported Variant conversion. Preserve the expected Updated value, diagnostics, fixtures and exclusions.

AB#646383

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3952f078-a881-4da8-ad96-13b727e48a91
Include the three-line assertion fix from PR #11340 so typed API-test discovery can compile CU 148339. Keep existing exclusions and authentication behavior unchanged.

AB#646383

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3952f078-a881-4da8-ad96-13b727e48a91
/// <summary>
/// Leaves API test requests configured with the transport's default authentication behavior.
/// </summary>
codeunit 131024 "No API Test Auth Provider" implements "API Test Auth Provider"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Breaking\ Changes}$

The new codeunit "No API Test Auth Provider" is public even though it looks like an implementation detail behind the public enum/interface-based authentication extension point. Leaving it public makes the concrete provider itself part of the supported API surface; mark the object internal and keep only the intended extensibility contract public.

Suggested fix (apply manually — could not be anchored as a one-click suggestion):

codeunit 131024 "No API Test Auth Provider" implements "API Test Auth Provider"
{
    Access = Internal;

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.41.6

if IsInitialized then
exit;

BindSubscription(APITestAuthProviderTests);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟠\ High\ Severity\ —\ Events}$

This test codeunit correctly uses a manual subscriber for scoped behavior, but it binds the global APITestAuthProviderTests instance in Initialize() and never unbinds it. Because that bound instance outlives the Initialize() call, later unrelated requests in the same session can still hit RecordFinalRequestEvent and keep mutating the recorder. Bind the subscriber only for each test's active scope, or add a matching UnbindSubscription when that scope ends.

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.41.6

…insic

Direct equality of WebServiceActionResultCode is rejected by AL0175. Format emits the explicit ALCompiler.ToNavValue system-option conversion; compare both formatted values while retaining the expected Updated code and existing diagnostics.

AB#646383

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3952f078-a881-4da8-ad96-13b727e48a91
Use the corrected Format-based assertions from PR #11340. Retain all authentication, initialization, exclusion and stack scope decisions.

AB#646383

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 3952f078-a881-4da8-ad96-13b727e48a91
/// <param name="Authentication">The authentication context to configure.</param>
procedure ConfigureAuthentication(var Authentication: Codeunit "API Test Auth Context")
var
Password: SecretText;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$\textbf{🟡\ Medium\ Severity\ —\ Security}$

The mock auth provider needlessly materializes the Basic-auth password in a plain Text local before converting it to SecretText. Keep the credential in SecretText from creation to the HTTP sink so it never becomes debugger-visible plain text.

Suggested fix (apply manually — could not be anchored as a one-click suggestion):

        Password: SecretText;
    begin
        InvocationCount += 1;
        APITestAuthRecorder.RecordCall(StrSubstNo(ProviderCallTok, InvocationCount));
        Password := SecretText.SecretStrSubstNo(PasswordTxt);

Knowledge:

👍 useful · ❤️ especially valuable · 👎 wrong - reply with why · AL review agent v1.41.6

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

AL: Apps (W1) Add-on apps for W1 Build: Automation Workflows and other setup in .github folder Build: scripts & configs Build scripts and configuration files ExpenseManagement Team: SCM GitHub request for SCM area

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants