Skip to content

feat: Test generation for modular SDK clients#3809

Merged
MaryGao merged 21 commits intomainfrom
copilot/fix-test-generation-conflicts
Mar 10, 2026
Merged

feat: Test generation for modular SDK clients#3809
MaryGao merged 21 commits intomainfrom
copilot/fix-test-generation-conflicts

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 3, 2026

Here is the design proposal for the PR description:


Design Proposal: Test Generation for Modular SDK Clients

Overview

This feature adds automatic .spec.ts test file generation for modular SDK clients from TypeSpec examples (x-ms-examples). It mirrors the existing samples generation infrastructure but targets recorded integration tests using @azure-tools/test-recorder.

Architecture

New Files

File Purpose
src/modular/emitTests.ts Entry point: orchestrates test file generation per operation
src/modular/helpers/exampleValueHelpers.ts Shared serialization utilities for both samples and tests
static/test-helpers/recordedClient.ts Static runtime helper: createRecorder() copied to test/generated/util/

Modified Files

File Change
src/modular/external-dependencies.ts Added AzureTestDependencies (Recorder, createTestCredential, assert, vitest hooks, env)
src/modular/static-helpers-metadata.ts Registered CreateRecorderHelpers.createRecorder refkey
src/framework/load-static-helpers.ts Loads static/test-helpers/test/generated/util/ when loadTestHelpers is set
src/framework/hooks/binder.ts Checks both tracked imports and getImportDeclarations() to avoid name conflicts
src/index.ts Calls emitTests() when generateTest && isAzurePackage

Generation Flow

TypeSpec spec + x-ms-examples
        │
        ▼
  emitTests(dpgContext)
        │  iterates clients × operations
        ▼
  emitMethodTests(method, options)
        │  per operation with examples
        ├── buildParameterValueMap()    → maps serialized param names to values
        ├── prepareCommonParameters()   → splits client-level vs method-level params
        ├── getCredentialTestValue()    → createTestCredential() for ARM/Azure
        ├── generateMethodCall()        → builds client.operationGroup.method(args)
        └── generateResponseAssertions() → assert.equal() for response fields
        │
        ▼
  test/generated/<fileName>.spec.ts

Generated Test Structure

Each generated test file follows this pattern:

describe("<operation description>", () => {
  let recorder: Recorder;
  let client: MyClient;

  beforeEach(async function(ctx) {
    recorder = await createRecorder(ctx);          // from static/test-helpers/
    const subscriptionId = env["SUBSCRIPTION_ID"] || "<default>";
    const credential = createTestCredential();      // from @azure-tools/test-credential
    const clientOptions = recorder.configureClientOptions({});
    client = new MyClient(credential, subscriptionId, clientOptions);
  });

  afterEach(async function() {
    await recorder.stop();
  });

  it("should <example name>", async function() {
    const result = await client.resourceGroup.get(...);
    assert.ok(result);
    assert.equal(result.name, "expectedName");   // from example response body
  });
});

Key Design Decisions

  1. Conditional loading: Test helpers (static/test-helpers/) are only copied when generateTest && isAzurePackage (production) or loadTestHelpers: true (unit tests). This avoids breaking the load-static-files integration test.

  2. Shared utilities: exampleValueHelpers.ts is shared between samples and tests. The key difference is that tests do NOT convert plainDate strings to Date objects — they stay as strings.

  3. Credential handling: For ARM/Azure services, tests use createTestCredential() from @azure-tools/test-credential to support both live and playback modes. Non-Azure services fall back to stub values.

  4. Response assertions: Simple scalar fields from the example response body generate assert.equal() statements. Complex/nested objects are skipped to keep tests maintainable.

  5. Binder import conflict resolution: The binder's generateLocallyUniqueImportName checks both tracked imports and sourceFile.getImportDeclarations() to handle the manual addImportDeclaration call for the client class.

Co-authored-by: MaryGao <9943211+MaryGao@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix merge conflicts in test generation for modular feat: Test generation for modular SDK clients Mar 3, 2026
@v-jiaodi
Copy link
Copy Markdown
Member

v-jiaodi commented Mar 3, 2026

@copilot Run pnpm format to fix the formatting issues, then run pnpm smoke-test under the typespec-test directory to update the code.

Co-authored-by: v-jiaodi <80496810+v-jiaodi@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 3, 2026

@copilot Run pnpm format to fix the formatting issues, then run pnpm smoke-test under the typespec-test directory to update the code.

Done in f271277. Ran pnpm format (fixed line-length formatting in load-static-helpers.ts and index.ts) and pnpm smoke-test (all specs succeeded — removed two now-obsolete get-binary-response-browser.mts generated files).

@v-jiaodi
Copy link
Copy Markdown
Member

v-jiaodi commented Mar 3, 2026

@copilot one issue found in our CI

  • integration testing failed and pls run npm run test:standard to produce.

…ilures

Co-authored-by: v-jiaodi <80496810+v-jiaodi@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 3, 2026

@copilot one issue found in our CI

  • integration testing failed and pls run npm run test:standard to produce.

Fixed in f513619. The root cause was that loadStaticHelpers was unconditionally loading test helpers from static/test-helpers/, which broke test-next/integration/load-static-files.test.ts (expected 1 source file, got 2). My previous fix made loading conditional on generateTest, but then testUtil.ts (used by unit test scenarios) had no way to trigger it. The solution:

  • Added a loadTestHelpers?: boolean option to LoadStaticHelpersOptions
  • testUtil.ts now passes loadTestHelpers: true explicitly
  • index.ts still drives it via generateTest && isAzurePackage

All tests now pass: 152 test-next, 860 unit tests, 559 RLC integration, 576 modular integration.

@MaryGao
Copy link
Copy Markdown
Member

MaryGao commented Mar 3, 2026

@copilot pls fix format issue

> npm run prettier -- --check


> @azure-tools/typespec-ts@0.50.0 prettier
> prettier --config ./.prettierrc "{src,test,static}/**/*.ts" "!test/**/generated/**/*.ts" --check

Checking formatting...
[warn] src/framework/load-static-helpers.ts
[warn] Code style issues found in the above file. Run Prettier with --write to fix.


Co-authored-by: MaryGao <9943211+MaryGao@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 3, 2026

@copilot pls fix format issue

...

Fixed in 4c2dd71 — ran npm run format to apply prettier formatting to load-static-helpers.ts.

@MaryGao MaryGao marked this pull request as ready for review March 3, 2026 07:41
@MaryGao MaryGao requested a review from Copilot March 3, 2026 07:42
@MaryGao
Copy link
Copy Markdown
Member

MaryGao commented Mar 3, 2026

@copilot please update the PR description with design proposal

@v-jiaodi
Copy link
Copy Markdown
Member

v-jiaodi commented Mar 4, 2026

@copilot revert commit 34c79d6

…nder.ts (reverts 34c79d6)

Co-authored-by: v-jiaodi <80496810+v-jiaodi@users.noreply.github.com>
Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 4, 2026

@copilot revert commit 34c79d6

Done in 626ad3d. Reverted commit 34c79d69 — removed the platform-specific browser helper variant logic from binder.ts and cleaned up the two get-binary-response-browser.mts files that were previously generated. Smoke test passes.

@v-jiaodi
Copy link
Copy Markdown
Member

v-jiaodi commented Mar 5, 2026

build generation test pass:
image

Comment thread packages/typespec-ts/src/index.ts Outdated
Comment thread packages/typespec-ts/src/index.ts Outdated
Comment thread packages/typespec-ts/src/modular/helpers/exampleValueHelpers.ts Outdated
@JialinHuang803
Copy link
Copy Markdown
Member

Is it possible to enable one smoke test (e.g. NetworkAnalytics.Management) to be generated with tests?

@MaryGao MaryGao force-pushed the copilot/fix-test-generation-conflicts branch from 0504e76 to 2f33bdc Compare March 10, 2026 06:37
Mary Gao added 3 commits March 10, 2026 14:41
…ration-conflicts

# Conflicts:
#	packages/typespec-ts/src/index.ts
#	packages/typespec-ts/test/util/testUtil.ts
@MaryGao
Copy link
Copy Markdown
Member

MaryGao commented Mar 10, 2026

Is it possible to enable one smoke test (e.g. NetworkAnalytics.Management) to be generated with tests?

I checked this is impossible in our smoke test since this is not in our SDK repo environment and enabling test would lead to build failure and JiaoDi has a test in SDK repo manually and see comment #3809 (comment)

@MaryGao MaryGao merged commit 278bac3 into main Mar 10, 2026
16 checks passed
@MaryGao MaryGao deleted the copilot/fix-test-generation-conflicts branch March 10, 2026 13:14
@kazrael2119
Copy link
Copy Markdown
Member

@copilot we met an issue

08:14:42.444 cmderr 	[automation_generate.sh] error @azure-tools/typespec-ts/file-formatting-error: Failed to format file: /mnt/vss/_work/1/s/azure-sdk-for-js/sdk/netapp/arm-netapp/test/generated/ransomwareReportsClearSuspectsTest.spec.ts. Error: SyntaxError: Unterminated string literal. (10:223)
08:14:42.444 cmderr 	[automation_generate.sh]    8 | import { beforeEach, afterEach, it, describe } from "vitest";
08:14:42.444 cmderr 	[automation_generate.sh]    9 |
08:14:42.444 cmderr 	[automation_generate.sh] > 10 | describe("clear ransomware suspects for the given Advanced Ransomware Protection report. You should evaluate the report to determine whether the activity is acceptable (false positive) or whether an attack seems malicious.
08:14:42.444 cmderr 	[automation_generate.sh]      |                                                                                                                                                                                                                               ^
08:14:42.445 cmderr 	[automation_generate.sh]   11 | ARP creates snapshots named Anti_ransomware_backup when it detects a potential ransomware threat. You can use one of these ARP snapshots or another snapshot of your volume to restore data",", () => {
08:14:42.445 cmderr 	[automation_generate.sh]   12 |   let recorder: Recorder;
08:14:42.445 cmderr 	[automation_generate.sh]   13 |   let client: NetAppManagementClient;.

seems this issue is from this feature,
here is the typespec:

@armResourceOperations
interface RansomwareReports {
  /**
   * Clear ransomware suspects for the given Advanced Ransomware Protection report. You should evaluate the report to determine whether the activity is acceptable (false positive) or whether an attack seems malicious.
   * ARP creates snapshots named Anti_ransomware_backup when it detects a potential ransomware threat. You can use one of these ARP snapshots or another snapshot of your volume to restore data",
   */
  clearSuspects is ArmResourceActionNoResponseContentAsync<
    RansomwareReport,
    RansomwareSuspectsClearRequest
  >;
}

analyze the root cause

JialinHuang803 pushed a commit to JialinHuang803/autorest.typescript that referenced this pull request Apr 9, 2026
* Initial plan

* feat: implement test generation for modular SDK clients

Co-authored-by: MaryGao <9943211+MaryGao@users.noreply.github.com>

* fix: run pnpm format and smoke-test to update generated code

Co-authored-by: v-jiaodi <80496810+v-jiaodi@users.noreply.github.com>

* fix: make test helpers loading conditional to fix integration test failures

Co-authored-by: v-jiaodi <80496810+v-jiaodi@users.noreply.github.com>

* fix: run prettier to fix formatting in load-static-helpers.ts

Co-authored-by: MaryGao <9943211+MaryGao@users.noreply.github.com>

* Update packages/typespec-ts/test/modularUnit/scenarios/test/operations/dpgOperationsTest.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update packages/typespec-ts/test/modularUnit/scenarios/test/operations/clientParameterTest.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update packages/typespec-ts/src/framework/hooks/binder.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update packages/typespec-ts/src/index.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update packages/typespec-ts/src/modular/helpers/exampleValueHelpers.ts

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix: compute relative src/index.js import path dynamically in emitTests for multi-client support

Co-authored-by: v-jiaodi <80496810+v-jiaodi@users.noreply.github.com>

* chore: run pnpm smoke-test to regenerate platform-specific browser helper variants

Co-authored-by: v-jiaodi <80496810+v-jiaodi@users.noreply.github.com>

* revert: remove platform-specific browser helper variant logic from binder.ts (reverts 34c79d6)

Co-authored-by: v-jiaodi <80496810+v-jiaodi@users.noreply.github.com>

* fix and update case

* Fix the build issues

* Remove the azure package condition since this is un-necessary

* Add unit test for test value assertion

* Fix the browser file missing issue

* Fxi wrong import issue

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: MaryGao <9943211+MaryGao@users.noreply.github.com>
Co-authored-by: v-jiaodi <80496810+v-jiaodi@users.noreply.github.com>
Co-authored-by: Mary Gao <yanmeigao1210@gmail.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Jiao Di (MSFT) <v-jiaodi@microsoft.com>
Co-authored-by: Mary Gao <marygao@microsoft.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants