-
Notifications
You must be signed in to change notification settings - Fork 79
Test Generation in Modular #3396
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- Added modular test generation similar to samples - Test names use sample description logic for consistency - Clean test names without trailing dots - Proper vitest/mocha framework support based on module type - Azure Identity integration for ARM clients - Generated test utilities including recordedClient.ts - Support for both single and multiple client scenarios - Unique test names including function names for multiple examples Generated 7 operation test files with professional naming: - assign role to the data product for dataProductsAddUserRoleMaximumSetGen - create data product resource for dataProductsCreateMaximumSetGen - delete data product resource for dataProductsDeleteMaximumSetGen - generate sas token for storage account for dataProductsGenerateStorageAccountSasTokenMaximumSetGen - list data products by resource group for dataProductsListByResourceGroupMaximumSetGenGeneratedByMinimumSetRuleMinimumSetGen - initiate key rotation on Data Product for dataProductsRotateKeyMaximumSetGen - update data product resource for dataProductsUpdateMaximumSetGen
Enhanced test generation to include response assertions that validate response values against expected values from examples: - Added generateResponseAssertions() function - Added generateAssertionsForValue() for recursive validation - Support for string, number, boolean, array, model, dict, null, union types - Proper handling of TypeSpec SDK numeric response indices - Validates key response properties with strictEqual checks - Array length and item validation - Model/object property validation up to 3 levels deep Generated tests now include comprehensive response validation like: assert.strictEqual(result.properties.resourceGuid, expected_value) assert.strictEqual(result.properties.provisioningState, expected_value) assert.ok(Array.isArray(result.dataTypeScope)) This ensures tests validate both API success and response content.
- Added 4 passing unit test scenarios for test generation: - basicOperationTest: Tests simple GET operation with response assertions - pagingOperationTest: Tests paging operations with array response validation - moduleTypeTest: Tests different import styles for ESM vs CommonJS - complexResponseTest: Tests nested object response assertions - Added emitTestsFromTypeSpec function to test/util/emitUtil.ts - Enhanced scenarios.spec.ts with 'tests' output block type - Improved test output formatting with proper file separation - All test scenarios validate comprehensive response assertions - Tests follow same pattern as existing samples scenarios
- Fix formatting issues in emitTests.ts (whitespace and comment styles) - Update scenarios.spec.ts with better test file handling and formatting fixes - Refresh test scenario files with updated expected outputs: - basicOperationTest.md: Updated with improved test structure - complexResponseTest.md: Updated response assertion patterns - moduleTypeTest.md: Updated with proper module import handling - pagingOperationTest.md: Updated paging operation test patterns - voidOperationTest.md: Updated void operation test handling All tests passing with SCENARIOS_UPDATE=true refresh
### LRO (Long Running Operations) Support: - Add detection for LRO operations (method.kind === 'lro' || method.kind === 'lropaging') - Generate proper poller pattern: 'const poller = await client.method()' followed by 'const result = await poller.pollUntilDone()' - Add comprehensive LRO test scenario (lroOperationTest.md) with ArmResourceCreateOrReplaceAsync example - Include proper response assertions on the final LRO result ### Paging Test Improvements: - Create dedicated generatePagingResponseAssertions() function - Fix incorrect paging assertions that were checking resArray[0].value instead of resArray directly - Now correctly asserts on collected results length and individual item properties - Updated pagingOperationTest.md with proper assertions ### Test Coverage Enhancement: - Fixed void operation test (voidOperationTest.md) to properly handle LRO delete operations - All 6 test scenarios now passing: basic, complex, module, paging, void, and LRO operations ### Technical Improvements: - Better separation of concerns between regular, paging, and LRO test generation - Proper understanding of how paging iteration works vs response structure - Correct LRO poller pattern following Azure SDK conventions
…torest.typescript into generate-test-modular
- Created shared utility module sampleTestHelpers.ts to reduce code duplication - Extracted common functions: buildParameterValueMap, prepareCommonValue, getCredentialSampleValue, getCredentialTestValue, serializeExampleValue - Unified CommonValue interface for both samples and tests - Refactored emitSamples.ts and emitTests.ts to use shared utilities - Eliminated ~200+ lines of duplicate code while maintaining functionality - All 388 modular unit tests continue to pass
…tilities - Created comprehensive sampleTestHelpers.ts module with shared utilities - Extracted common functions: prepareCommonParameters, iterateClientsAndMethods, generateMethodCall, createSourceFile, generateAssertionsForValue, generateResponseAssertions - Reduced code duplication by ~200+ lines across emit files - Enhanced test vs sample differentiation (env variables for tests, example values for samples) - Improved maintainability with unified parameter preparation and assertion generation - All 388 modular unit tests passing after refactoring Breaking down the previous monolithic emit functions into: 1. Common parameter handling logic shared between samples and tests 2. Unified client/method iteration patterns 3. Shared assertion generation for response validation 4. Consistent source file creation and import handling 5. Better separation of concerns between sample and test generation This refactoring eliminates significant code duplication while maintaining 100% test compatibility.
There was a problem hiding this comment.
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 introduces experimental test generation functionality for the TypeSpec TypeScript emitter. It adds the ability to automatically generate test files from TypeSpec examples, similar to the existing sample generation feature, with proper test recorder integration and response assertions.
Key Changes
- Added new test generation infrastructure with
emitTests.tsand shared helper utilities inexampleValueHelpers.ts - Introduced static test helper for recorder client setup (
recordedClient.ts) - Added extensive unit test scenarios covering parameters, responses, and various operation types
- Updated the binder to handle test-specific helper files
Reviewed Changes
Copilot reviewed 33 out of 33 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| packages/typespec-ts/src/modular/emitTests.ts | New file implementing test generation logic with vitest integration |
| packages/typespec-ts/src/modular/helpers/exampleValueHelpers.ts | New shared utilities for both samples and tests, including parameter processing and assertion generation |
| packages/typespec-ts/src/modular/emitSamples.ts | Refactored to use shared helpers from exampleValueHelpers.ts |
| packages/typespec-ts/src/modular/external-dependencies.ts | Added AzureTestDependencies for vitest and test-recorder imports |
| packages/typespec-ts/src/modular/static-helpers-metadata.ts | Added CreateRecorderHelpers metadata |
| packages/typespec-ts/static/test-helpers/recordedClient.ts | New static helper for test recorder setup |
| packages/typespec-ts/src/framework/load-static-helpers.ts | Enhanced to load test helpers from test-helpers directory |
| packages/typespec-ts/src/framework/hooks/binder.ts | Updated to clean unused helper files in test directories |
| packages/typespec-ts/src/lib.ts | Added experimental-generate-test-files option to schema |
| packages/typespec-ts/src/index.ts | Integrated test generation into emit pipeline |
| packages/typespec-ts/test/util/testUtil.ts | Added test dependencies to test utilities |
| packages/typespec-ts/test/util/emitUtil.ts | Added emitTestsFromTypeSpec helper function |
| packages/typespec-ts/test/modularUnit/scenarios.spec.ts | Added test generation support to scenario testing |
| packages/typespec-ts/test/modularUnit/scenarios/test/**/*.md | New test scenario files covering various parameter and operation types |
| packages/typespec-ts/test-next/integration/load-static-files.test.ts | Updated expected file count for additional helper file |
Comments suppressed due to low confidence (2)
packages/typespec-ts/src/framework/load-static-helpers.ts:1
- Typo in comment: 'normalizae' should be 'normalize' in the comment on line 324.
import { readdir, stat, readFile } from "fs/promises";
packages/typespec-ts/src/framework/load-static-helpers.ts:1
- Typo in comment: 'normalizae' should be 'normalize'.
import { readdir, stat, readFile } from "fs/promises";
| return { | ||
| ...defaultSetting, | ||
| value: `{ getToken: async () => { | ||
| return { token: "INPUT_YOUR_TOKEN_HERE", expiresOnTimestamp: now() }; } }` |
Copilot
AI
Oct 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function now() is undefined. Should be Date.now() for the timestamp.
| return { token: "INPUT_YOUR_TOKEN_HERE", expiresOnTimestamp: now() }; } }` | |
| return { token: "INPUT_YOUR_TOKEN_HERE", expiresOnTimestamp: Date.now() }; } }` |
| value: `{ | ||
| getToken: async () => { | ||
| return { token: "INPUT_YOUR_TOKEN_HERE", expiresOnTimestamp: Date.now() }; | ||
| } | ||
| } ` |
Copilot
AI
Oct 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] Inconsistent formatting: this credential value has extra spaces and different indentation compared to the sample version on line 120-122. Consider using the same formatting for both.
| value: `{ | |
| getToken: async () => { | |
| return { token: "INPUT_YOUR_TOKEN_HERE", expiresOnTimestamp: Date.now() }; | |
| } | |
| } ` | |
| value: `{ getToken: async () => ({ token: "INPUT_YOUR_TOKEN_HERE", expiresOnTimestamp: Date.now() }) }` |
|
|
||
| ${beforeEachType}(async function(ctx) { | ||
| recorder = await ${createRecorderHelper}(ctx); | ||
| ${clientParameterDefs.join("\n")} |
Copilot
AI
Oct 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The clientParameterDefs array elements are not being indented when joined. This will cause the generated code to have inconsistent indentation. Consider adding proper indentation: ${clientParameterDefs.join('\\n ')}
| ${clientParameterDefs.join("\n")} | |
| ${clientParameterDefs.join("\n ")} |
| const assertions: string[] = []; | ||
|
|
||
| // Prevent infinite recursion for deeply nested objects | ||
| if (currentDepth >= maxDepth) { | ||
| return assertions; | ||
| } |
Copilot
AI
Oct 29, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function generateAssertionsForValue has complex recursive logic with a maxDepth parameter, but lacks a JSDoc comment explaining the purpose of this parameter, the recursion depth limit, and when it would be hit. Consider adding comprehensive documentation.
|
@jiaodi Could you help resolve the conflicts? |
fixes #3247 #3371 #3345