Closed spetrescu84 closed 2 weeks ago
Cloned PR for spetrescu/merge_main_1.5.1_to_dev
Code cleanup
This pull request re-enables a previously commented-out unit test in the MSIDThrottlingServiceIntegrationTests.m
file.
MSIDThrottlingServiceIntegrationTests.m
: Re-enabled the test testMSIDThrottlingServiceIntegration_SSOSilentRequestThatReturnsInteractionRequiredError_ShouldBeThrottledSuccessfully_AndThenUnThrottledUponLaterSuccessfulInteractionRequest
by uncommenting and restoring its implementation."To review the method testMSIDLRUCache_whenCallingAPIsUseThrottlingCacheWithinGCDBlocks_throttlingCacheShouldPerformOperationsWithThreadSafety
, let's break it down step-by-step:
subError
variable is shared across multiple threads without synchronization, which can lead to race conditions.- (void)testMSIDLRUCache_whenCallingAPIsUseThrottlingCacheWithinGCDBlocks_throttlingCacheShouldPerformOperationsWithThreadSafety
{
dispatch_queue_t parentQ1 = dispatch_queue_create(\"parentQ1\", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t parentQ2 = dispatch_queue_create(\"parentQ2\", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();
XCTestExpectation *expectation1 = [[XCTestExpectation alloc] initWithDescription:@\"Calling API1\"];
XCTestExpectation *expectation2 = [[XCTestExpectation alloc] initWithDescription:@\"Calling API2\"];
XCTestExpectation *expectation3 = [[XCTestExpectation alloc] initWithDescription:@\"Calling API3\"];
XCTestExpectation *expectation4 = [[XCTestExpectation alloc] initWithDescription:@\"Calling API4\"];
NSArray *expectationsAdd = @[expectation1, expectation2];
NSArray *expectationsRemove = @[expectation3, expectation4];
MSIDLRUCache *customLRUCache = [[MSIDLRUCache alloc] initWithCacheSize:100];
__block NSError *subError = nil;
__block NSError *threadSafeError = nil;
dispatch_group_async(group, parentQ1, ^{
for (int i = 0; i < 50; i++)
{
NSString *cacheKey = [NSString stringWithFormat:@\"%i\", i];
MSIDThrottlingCacheRecord *throttleCacheRecord = [[MSIDThrottlingCacheRecord alloc] initWithErrorResponse:nil
throttleType:i
throttleDuration:100];
[customLRUCache setObject:throttleCacheRecord
forKey:cacheKey
error:&subError];
if (subError) {
@synchronized (self) {
threadSafeError = subError;
}
}
}
[expectation1 fulfill];
});
dispatch_group_async(group, parentQ2, ^{
for (int i = 50; i < 100; i++)
{
NSString *cacheKey = [NSString stringWithFormat:@\"%i\", i];
MSIDThrottlingCacheRecord *throttleCacheRecord = [[MSIDThrottlingCacheRecord alloc] initWithErrorResponse:nil
throttleType:i
throttleDuration:100];
[customLRUCache setObject:throttleCacheRecord
forKey:cacheKey
error:&subError];
if (subError) {
@synchronized (self) {
threadSafeError = subError;
}
}
}
[expectation2 fulfill];
});
[self waitForExpectations:expectationsAdd timeout:20];
XCTAssertEqual(customLRUCache.numCacheRecords, 100);
dispatch_group_async(group, parentQ1, ^{
for (int i = 0; i < 50; i++)
{
NSString *cacheKey = [NSString stringWithFormat:@\"%i\", i];
NSString *cacheKeyFromOtherQueue = [NSString stringWithFormat:@\"%i\", (i + 50)];
if (i % 2)
{
[customLRUCache removeObjectForKey:cacheKey error:&subError];
if (subError) {
@synchronized (self) {
threadSafeError = subError;
}
}
MSIDThrottlingCacheRecord *throttleCacheRecord = [[MSIDThrottlingCacheRecord alloc] initWithErrorResponse:nil
throttleType:(i + 50)
throttleDuration:100];
[customLRUCache setObject:throttleCacheRecord forKey:cacheKeyFromOtherQueue error:&subError];
if (subError) {
@synchronized (self) {
threadSafeError = subError;
}
}
}
}
[expectation3 fulfill];
});
dispatch_group_async(group, parentQ2, ^{
for (int i = 50; i < 100; i++)
{
NSString *cacheKey = [NSString stringWithFormat:@\"%i\", i];
NSString *cacheKeyFromOtherQueue = [NSString stringWithFormat:@\"%i\", (i - 50)];
if (i % 2)
{
[customLRUCache removeObjectForKey:cacheKey error:&subError];
if (subError) {
@synchronized (self) {
threadSafeError = subError;
}
}
MSIDThrottlingCacheRecord *throttleCacheRecord = [[MSIDThrottlingCacheRecord alloc] initWithErrorResponse:nil
throttleType:(i - 50)
throttleDuration:100];
[customLRUCache setObject:throttleCacheRecord forKey:cacheKeyFromOtherQueue error:&subError];
if (subError) {
@synchronized (self) {
threadSafeError = subError;
}
}
}
}
[expectation4 fulfill];
});
[self waitForExpectations:expectationsRemove timeout:20];
XCTAssertEqual(customLRUCache.numCacheRecords, customLRUCache.cacheAddCount - customLRUCache.cacheRemoveCount);
XCTAssertNil(threadSafeError);
}
This refactored code ensures thread safety, proper error handling, and better readability. AI-generated content may be incorrect
"The code change snippet indicates the removal of the method testMSIDThrottlingServiceIntegration_SSOSilentRequestThatReturnsInteractionRequiredError_ShouldBeThrottledSuccessfully_AndThenUnThrottledUponLaterSuccessfulInteractionRequest
. Since the method is removed, there is no need to review it for logical or syntactical bugs or suggest refactoring.
However, if you want to review the entire file for potential improvements, here are some general suggestions:
Refactor Long Methods: Some methods are quite long and could be broken down into smaller, more manageable methods. For example, the testMSIDThrottlingServiceIntegration_NonSSOSilentRequestThatReturns429Response_ShouldBeThrottledByThrottlingService
method could be split into smaller methods for setting up the test, making the request, and verifying the results.
Use Constants for Repeated Strings: Strings like @\"429\"
, @\"Retry-After\"
, and @\"100\"
are repeated multiple times. Consider defining them as constants to avoid magic numbers and strings.
Remove Unused Imports: Ensure that all imported headers are necessary. Removing unused imports can make the code cleaner and reduce compilation time.
Consistent Error Handling: Ensure that error handling is consistent across all methods. For example, some methods check for errors and handle them, while others do not.
Use Helper Methods for Common Logic: If there is common logic used across multiple tests, consider extracting it into helper methods. This can reduce code duplication and make the tests easier to maintain.
Add Comments for Complex Logic: Adding comments to explain complex logic can make the code easier to understand for future maintainers.
Here is an example of how you might refactor one of the long test methods:
- (void)testMSIDThrottlingServiceIntegration_NonSSOSilentRequestThatReturns429Response_ShouldBeThrottledByThrottlingService
{
MSIDDefaultSilentTokenRequest *defaultSilentTokenRequest = [self createDefaultSilentTokenRequest];
MSIDRefreshToken *refreshToken = [self createRefreshToken];
MSIDThrottlingServiceMock *throttlingServiceMock = [self createThrottlingServiceMock];
defaultSilentTokenRequest.throttlingService = throttlingServiceMock;
[self swizzleTokenEndpoint];
MSIDTestURLResponse *tokenResponse = [self createTokenResponseWithCode:429 retryAfter:@\"100\"];
[self performSilentRequest:defaultSilentTokenRequest withRefreshToken:refreshToken expectedThrottleCount:0 expectedUpdateCount:1];
[self verifyThrottlingCacheForKey:@\"9671032187006166342\" expectedThrottleType:MSIDThrottlingType429];
[self performSilentRequest:defaultSilentTokenRequest withRefreshToken:refreshToken expectedThrottleCount:1 expectedUpdateCount:1];
}
- (MSIDDefaultSilentTokenRequest *)createDefaultSilentTokenRequest
{
return [[MSIDDefaultSilentTokenRequest alloc] initWithRequestParameters:self.silentRequestParameters
forceRefresh:NO
oauthFactory:[MSIDAADV2Oauth2Factory new]
tokenResponseValidator:[MSIDDefaultTokenResponseValidator new]
tokenCache:self.tokenCache
accountMetadataCache:self.accountMetadataCache];
}
- (MSIDRefreshToken *)createRefreshToken
{
MSIDRefreshToken *refreshToken = [[MSIDRefreshToken alloc] init];
refreshToken.refreshToken = self.refreshToken;
return refreshToken;
}
- (MSIDThrottlingServiceMock *)createThrottlingServiceMock
{
return [[MSIDThrottlingServiceMock alloc] initWithDataSource:self.keychainTokenCache context:self.silentRequestParameters];
}
- (void)swizzleTokenEndpoint
{
[MSIDTestSwizzle instanceMethod:@selector(tokenEndpoint)
class:[MSIDRequestParameters class]
block:(id)^(void) {
return [[NSURL alloc] initWithString:DEFAULT_TEST_TOKEN_ENDPOINT_GUID];
}];
}
- (MSIDTestURLResponse *)createTokenResponseWithCode:(NSInteger)code retryAfter:(NSString *)retryAfter
{
MSIDTestURLResponse *tokenResponse = [MSIDTestURLResponse refreshTokenGrantResponseForThrottling:self.refreshToken
requestClaims:self.atRequestClaim
requestScopes:self.oidcScopeString
responseAT:@\"new at\"
responseRT:self.refreshToken
responseID:nil
responseScope:@\"user.read tasks.read\"
responseClientInfo:nil
url:DEFAULT_TEST_TOKEN_ENDPOINT_GUID
responseCode:code
expiresIn:nil
enrollmentId:@\"adf79e3f-mike-454d-9f0f-2299e76dbfd5\"
redirectUri:self.redirectUri
clientId:self.silentRequestParameters.clientId];
tokenResponse->_error = MSIDCreateError(MSIDErrorDomain, MSIDErrorInternal, @\"429 error test\", @\"oAuthError\", @\"subError\", nil, nil, @{MSIDHTTPResponseCodeKey : @(code).stringValue, MSIDHTTPHeadersKey: @{@\"Retry-After\": retryAfter}}, NO);
return tokenResponse;
}
- (void)performSilentRequest:(MSIDDefaultSilentTokenRequest *)request withRefreshToken:(MSIDRefreshToken *)refreshToken expectedThrottleCount:(NSUInteger)throttleCount expectedUpdateCount:(NSUInteger)updateCount
{
XCTestExpectation *expectation = [self expectationWithDescription:@\"silent request\"];
[request acquireTokenWithRefreshTokenImpl:refreshToken completionBlock:^(MSIDTokenResult * _Nullable result, NSError * _Nullable error) {
XCTAssertEqual(request.throttlingService.shouldThrottleRequestInvokedCount, throttleCount);
XCTAssertEqual(request.throttlingService.updateThrottlingServiceInvokedCount, updateCount);
XCTAssertNil(result);
XCTAssertNotNil(error);
XCTAssertEqual(error.code, MSIDErrorInternal);
[expectation fulfill];
}];
[self waitForExpectationsWithTimeout:5.0 handler:nil];
}
- (void)verifyThrottlingCacheForKey:(NSString *)key expectedThrottleType:(MSIDThrottlingType)throttleType
{
NSError *subError = nil;
MSIDThrottlingCacheRecord *record = [[MSIDLRUCache sharedInstance] objectForKey:key error:&subError];
XCTAssertNotNil(record);
XCTAssertNil(subError);
XCTAssertEqual(record.throttleType, throttleType);
XCTAssertEqual(record.cachedErrorResponse.code, MSIDErrorInternal);
XCTAssertEqual(record.cachedErrorResponse.domain, MSIDErrorDomain);
XCTAssertEqual(record.throttledCount, 1);
}
This refactoring breaks down the long test method into smaller, more manageable methods, making the code easier to read and maintain. AI-generated content may be incorrect
"### Review and Suggestions
The code snippet provided is a single line:
sequence:nil];
This line is part of a method call, specifically:
msidParams.platformSequence = [NSString msidUpdatePlatformSequenceParamWithSrcName:[MSIDVersion platformName]
srcVersion:[MSIDVersion sdkVersion]
sequence:nil];
Logical Review:
msidUpdatePlatformSequenceParamWithSrcName:srcVersion:sequence:
is being called with sequence:nil
. This implies that the sequence
parameter is being set to nil
. This might be intentional, but it is important to ensure that the method can handle a nil
value for this parameter without causing any issues.Syntactical Review:
To improve readability and maintainability, consider the following refactoring suggestions:
Extract Method Call into a Variable:
nil
value.Add Comments:
nil
is being passed to the sequence
parameter.Here is the refactored code with these suggestions:
// Update platform sequence parameter with source name, version, and sequence
NSString *platformSequence = [NSString msidUpdatePlatformSequenceParamWithSrcName:[MSIDVersion platformName]
srcVersion:[MSIDVersion sdkVersion]
sequence:nil]; // sequence is set to nil intentionally
msidParams.platformSequence = platformSequence;
"### Plan
jobs
section for logical and syntactical errors.Job Validate_Pull_Request
:
maxParallel
should be maxParallelism
.timeOutInMinutes
should be timeoutInMinutes
.Job fetch_pr_labels
:
Job e2e_test_native_auth_ios
and e2e_test_native_auth_mac
:
Job Validate_SPM_Integration
:
timeOutInMinutes
should be timeoutInMinutes
.jobs:
- job: 'Validate_Pull_Request'
strategy:
maxParallelism: 3
matrix:
IOS_FRAMEWORK:
target: \"iosFramework iosTestApp sampleIosApp sampleIosAppSwift\"
MAC_FRAMEWORK:
target: \"macFramework\"
VISION_FRAMEWORK:
target: \"visionOSFramework\"
displayName: Validate Pull Request
pool:
vmImage: 'macOS-14'
timeoutInMinutes: 30
steps:
- script: |
/bin/bash -c \"sudo xcode-select -s /Applications/Xcode_15.4.app\"
displayName: 'Switch to use Xcode 15.4'
- task: CmdLine@2
displayName: Installing dependencies
inputs:
script: |
gem install xcpretty slather bundler -N
failOnStderr: true
- checkout: self
clean: true
submodules: true
fetchDepth: 1
persistCredentials: false
- task: Bash@3
displayName: Removing any lingering codecov files
inputs:
targetType: 'inline'
script: |
find . -name \"*.gcda\" -print0 | xargs -0 rm
- task: ComponentGovernanceComponentDetection@0
inputs:
alertWarningLevel: Low
- task: Bash@3
displayName: download visionOS SDK
inputs:
targetType: 'inline'
script: |
echo $(target)
if [ $(target) == 'visionOSFramework' ]; then
echo \"Downloading simulator for visionOS\"
sudo xcode-select -s /Applications/Xcode_15.4.app/Contents/Developer
defaults write com.apple.dt.Xcode AllowUnsupportedVisionOSHost -bool YES
defaults write com.apple.CoreSimulator AllowUnsupportedVisionOSHost -bool YES
xcodebuild -downloadPlatform visionOS
else
echo \"Not visionOS job, no download needed\"
fi
failOnStderr: false
- task: Bash@3
displayName: Run Build script & check for Errors
inputs:
targetType: 'inline'
script: |
{ output=$(./build.py --target $(target) 2>&1 1>&3-) ;} 3>&1
final_status=$(<./build/status.txt)
echo \"FINAL STATUS = ${final_status}\"
echo \"POSSIBLE ERRORS: ${output}\"
if [ $final_status != \"0\" ]; then
echo \"Build & Testing Failed!
${output}\" >&2
fi
failOnStderr: true
- task: Bash@3
condition: always()
displayName: Cleanup
inputs:
targetType: 'inline'
script: |
rm -rf ./build/status.txt
- task: PublishTestResults@2
condition: always()
displayName: Publish Test Report
inputs:
testResultsFormat: 'JUnit'
testResultsFiles: '$(Agent.BuildDirectory)/s/build/reports/*'
failTaskOnFailedTests: true
testRunTitle: 'Test Run - $(target)'
- job: fetch_pr_labels
displayName: 'Check for PR Label'
timeoutInMinutes: 5
pool:
vmImage: 'macOS-14'
steps:
- script: |
url=\"https://api.github.com/repos/$BUILD_REPOSITORY_ID/issues/$SYSTEM_PULLREQUEST_PULLREQUESTNUMBER/labels\"
echo \"Fetching labels from $url \"
temp_file=$(mktemp)
response_code=$(curl -s -w \"%{http_code}\" -o \"$temp_file\" \"$url\")
response_content=$(cat \"$temp_file\")
echo \"Response code: $response_code\"
echo \"Raw response: $response_content\"
if [[ \"$response_code\" -eq 200 ]]; then
label_names=$(echo $response_content | jq -r '.[].name' | paste -sd ', ' -)
echo \"##vso[task.setvariable variable=PR_LABELS;isOutput=true]$label_names\"
[ -z \"$label_names\" ] && echo \"PR labels: \" || echo \"PR labels: $label_names\"
else
echo \"Request failed with status code: $response_code - Skipping Native Auth E2E tests as a preventive measure\"
echo \"##vso[task.setvariable variable=PR_LABELS;isOutput=true]'skip-native-auth-e2e-tests'\"
fi
name: fetchPrLabels
- job: e2e_test_native_auth_ios
displayName: 'Run MSAL E2E tests for iOS native auth'
dependsOn: fetch_pr_labels
condition: and(succeeded(), not(contains(dependencies.fetch_pr_labels.outputs['fetchPrLabels.PR_LABELS'], 'skip-native-auth-e2e-tests')))
timeoutInMinutes: 30
cancelTimeoutInMinutes: 5
pool:
vmImage: 'macOS-14'
workspace:
clean: all
steps:
- template: templates/tests-with-conf-file.yml
parameters:
schema: 'MSAL iOS Native Auth E2E Tests'
full_path: 'build/Build/Products/MSAL iOS Native Auth E2E Tests_MSAL iOS Native Auth E2E Tests_iphonesimulator17.5-x86_64.xctestrun'
destination: 'platform=iOS Simulator,name=iPhone 15,OS=17.5'
sdk: 'iphonesimulator'
- job: e2e_test_native_auth_mac
displayName: 'Run MSAL E2E tests for macOS native auth'
dependsOn: fetch_pr_labels
condition: and(succeeded(), not(contains(dependencies.fetch_pr_labels.outputs['fetchPrLabels.PR_LABELS'], 'skip-native-auth-e2e-tests')))
timeoutInMinutes: 30
cancelTimeoutInMinutes: 5
pool:
vmImage: 'macOS-14'
workspace:
clean: all
steps:
- template: templates/tests-with-conf-file.yml
parameters:
schema: 'MSAL Mac Native Auth E2E Tests'
full_path: 'build/Build/Products/MSAL Mac Native Auth E2E Tests_MSAL Mac Native Auth E2E Tests_macosx14.5-x86_64.xctestrun'
destination: 'platform=macOS'
sdk: 'macosx'
- job: 'Validate_SPM_Integration'
displayName: Validate SPM Integration
pool:
vmImage: 'macOS-13'
timeoutInMinutes: 15
workspace:
clean: all
steps:
- checkout: self
clean: true
submodules: true
fetchDepth: 1
persistCredentials: true
path: s
- script: |
/bin/bash -c \"sudo xcode-select -s /Applications/Xcode_14.3.app\"
displayName: 'Switch to use Xcode 14.3'
- task: Bash@3
displayName: Set variable BRANCH_NAME to a temporary branch
inputs:
targetType: 'inline'
script: |
BRANCH_NAME_LOCAL=\"$(Build.SourceBranchName)-temp\"
echo \"##vso[task.setvariable variable=BRANCH_NAME]${BRANCH_NAME_LOCAL}\"
- task: Bash@3
displayName: Checkout to temporary branch
inputs:
targetType: 'inline'
script: |
git checkout -b \"${BRANCH_NAME}\"
- task: Bash@3
displayName: Run SPM integration test script
inputs:
targetType: 'inline'
script: |
sh spm-integration-test.sh \"${BRANCH_NAME}\"
continueOnError: false
- task: Bash@3
condition: always()
displayName: Cleanup
inputs:
targetType: 'inline'
script: |
cd ../..
rm -rf \"$SAMPLE_APP_TEMP_DIR\" archive framework MSAL.zip
git checkout -- .
git fetch --quiet
git switch \"$(Build.SourceBranchName)\"
git branch -D \"$BRANCH_NAME\"
git push origin --delete \"$BRANCH_NAME\"
maxParallel
to maxParallelism
and timeOutInMinutes
to timeoutInMinutes
.
Proposed changes
Merge main after 1.5.1 release back to dev
Type of change
Risk
Additional information