-
Notifications
You must be signed in to change notification settings - Fork 645
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
Functional test that verifies the order of packages in the feed #3398
Conversation
/// Deletes (unlists) a package with the specified Id and Version and checks if the delete has succeeded. | ||
/// This will be used by test classes which tests scenarios on top of delete/unlist. | ||
/// </summary> | ||
public async Task DeletePackageAndVerify(string packageId, string version = "1.0.0") |
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.
UnlistPackage. Otherwise it's not clear. Lets stick to "Gallery" language, which is unlist, as opposed to client language, which is delete
processResult.ExitCode + ". Error message: \"" + processResult.StandardError + "\""); | ||
} | ||
|
||
public void VerifyPackage(string packageId, string version = "1.0.0") |
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.
Not a very good name, since it's not clear what is being verified.
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.
I agree. I split the function above from UploadNewPackageAndVerify
into UploadNewPackage
and VerifyPackage
so I took the name and didn't pay much attention.
{ | ||
await DeletePackage(packageId, version); | ||
|
||
VerifyPackage(packageId, version); |
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.
Shouldn't we verify the package does not exist?
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 package still exists there, it's just been unlisted. I previously had added logic within VerifyPackage
to check if the IPackage
object had IsListed
set to true for upload and false for delete but it seemed to always be false, regardless of the context, so I removed it.
@@ -59,17 +59,60 @@ public ODataHelper(ITestOutputHelper testOutputHelper) | |||
} | |||
} | |||
|
|||
public async Task<bool> ContainsResponseText(string url, params string[] expectedTexts) | |||
public async Task<DateTime> GetTimestampOfPackageFromResponse(string url, string propertyName, string packageId, string version = "1.0.0") |
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.
we shouldn't have a default value for version here (this file).
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.
Why not? I like having the default because then I can ignore version and operate on just a package id.
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.
because this is a utilities class. It should be general, and not assume values.
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.
I disagree. It's a utilities class for writing tests and having the default value makes writing the tests simpler. Also, to change this would require modifying code in a lot of places. I don't really see much value changing this.
{ | ||
responseText = await sr.ReadToEndAsync().ConfigureAwait(false); | ||
return DateTime.MinValue; |
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.
you should return NULL in this case, since this is a failure. DateTime.MinValue might be a valid response
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.
null
is not a DateTime
should i throw an exception instead?
if (timestampStartIndex < 0) | ||
{ | ||
WriteLine($"Package data does not contain '{propertyName}' timestamp!"); | ||
return DateTime.MinValue; |
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.
same
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.
null is not a DateTime
should i throw an exception instead?
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.
You can return DateTime? , I didn't see code here that throws exceptions, so dont think its a good idea to introduce this now
string responseText; | ||
using (var sr = new StreamReader(response.GetResponseStream())) | ||
var packageResponse = await GetPackageDataInResponse(url, packageId, version); | ||
if (string.IsNullOrEmpty(packageResponse)) | ||
{ |
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.
Add a message that there's no response
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.
GetPackageDataInResponse
already does this.
var timestampEndTag = "</d:" + propertyName + ">"; | ||
var timestampStartIndex = packageResponse.IndexOf(timestampStartTag) + timestampStartTag.Length; | ||
|
||
if (timestampStartIndex < 0) |
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.
packageResponse.IndexOf(timestampStartTag) < 0
@@ -56,6 +59,122 @@ public V2FeedExtendedTests(ITestOutputHelper testOutputHelper) | |||
} | |||
} | |||
|
|||
private const int PackagesInOrderNumPackages = 10; |
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.
can we make this lower? like 3?
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.
doing so significantly reduces the chance of seeing the issue...the runtime of the test doesn't really scale with number of packages, it mostly scales with the delay in the search service
EDIT: now that we are no longer hitting the search service, the number of packages is now the primary factor scaling the runtime of the test, but the runtime is minimal enough for it to be a non-issue.
Ran this against dev with the fix implemented, works perfectly! |
{ | ||
if (string.IsNullOrEmpty(packageId)) | ||
{ | ||
packageId = DateTime.Now.Ticks.ToString(); |
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.
If it is used current date time, the package will not be found. Why not just fail is null or empty?
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.
That's a very good point. I took the code from here, but I agree that this should just be a throw new ArgumentNullException
.
using (var sr = new StreamReader(response.GetResponseStream())) | ||
{ | ||
responseText = await sr.ReadToEndAsync().ConfigureAwait(false); | ||
} |
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.
Why is needed the ConfigureAwait? If it is not needed I would suggest to remove it.
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.
@@ -110,11 +110,20 @@ public bool CheckIfPackageVersionExistsInSource(string packageId, string version | |||
} | |||
|
|||
/// <summary> | |||
/// Creates a package with the specified Id and Version and uploads it and checks if the upload has suceeded. | |||
/// Creates a package with the specified Id and Version and uploads it and checks if the upload has succeeded. | |||
/// This will be used by test classes which tests scenarios on top of upload. | |||
/// </summary> | |||
public async Task UploadNewPackageAndVerify(string packageId, string version = "1.0.0", string minClientVersion = null, string title = null, string tags = null, string description = null, string licenseUrl = null, string dependencies = null) | |||
{ |
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.
How is the verification result returned to the user? Should it be a the return value a Task instead of Task?
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.
It throws if the verification fails. I will add a comment.
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.
Had an offline talk with Scott. This PR doesn't fully/sufficiently check the issue with the feed ordering, because we are inserting/editing the packages synchronously, we need to have the functional test to validate the feed2Catalog doesn't miss the edits done asynchronously to have the complete picture. Given that, from what Scott said, the issue happened because the instance's local time was off by couple of seconds, this could work if executed fast enough, but that is just a conjecture.
I would check if this test fails with the bug that caused out of ordering and if it works with it, it'd be fine to have this as a general may be it will catch the issue test.
@@ -56,6 +64,133 @@ public V2FeedExtendedTests(ITestOutputHelper testOutputHelper) | |||
} | |||
} | |||
|
|||
private const int PackagesInOrderNumPackages = 100; |
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.
10
var triesAvailable = PackagesInOrderNumRetries; | ||
|
||
// Upload the packages. | ||
var uploadStartTimestamp = DateTime.UtcNow.AddMinutes(-1); |
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.
move this to top so that it would be before starting time.
return null; | ||
} | ||
|
||
var timestampStartTag = "<d:" + propertyName + " m:type=\"Edm.DateTime\">"; |
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.
may be use XML Parser, don't need to rely on this syntax here.
@@ -56,6 +64,133 @@ public V2FeedExtendedTests(ITestOutputHelper testOutputHelper) | |||
} | |||
} | |||
|
|||
private const int PackagesInOrderNumPackages = 100; | |||
private const int PackagesInOrderNumRetries = 12; |
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.
Reduce retries to 5.
Assert.True(lastEditedTimestamp.Value > lastLastEditedTimestamp, | ||
$"Package #{i} was edited after package #{i - 1} but has an earlier LastEdited timestamp ({lastEditedTimestamp} should be greater than {lastLastEditedTimestamp})."); | ||
lastLastEditedTimestamp = lastEditedTimestamp.Value; | ||
}, triesAvailable, PackagesInOrderRefreshTimeMs); |
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.
move the async function to a separate method and reuse above.
packageId = DateTime.Now.Ticks.ToString(); | ||
} | ||
|
||
WriteLine("Deleting package '{0}', version '{1}'", packageId, version); |
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.
Unlisting package...
var processResult = await commandlineHelper.DeletePackageAsync(packageId, version, UrlHelper.V2FeedPushSourceUrl); | ||
|
||
Assert.True(processResult.ExitCode == 0, | ||
"The package delete via Nuget.exe did not succeed properly. Check the logs to see the process error and output stream. Exit Code: " + |
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.
package unlisted ...
Assert.True(createdTimestamp.HasValue); | ||
Assert.True(createdTimestamp.Value > uploadStartTimestamp); | ||
Assert.True(createdTimestamp.Value > lastCreatedTimestamp, | ||
$"Package #{i} was uploaded after package #{i - 1} but has an earlier Created timestamp ({createdTimestamp} should be greater than {lastCreatedTimestamp})."); |
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.
Do we guarantee that ordering of requests?
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.
Because the packages are uploaded/unlisted synchronously and we wait for the request to complete, we know that they must be in that order.
@shishirx34 I have added an explicit comment to the top of the test case in honor of the discussion we had offline. |
Idea behind the test case is this:
x
packages synchronously (wait for each request to finish before starting the next one).Created
timestamps of the packages in the feed are in the correct order.LastEdited
timestamps of the packages in the feed are in the correct order.Had to make some modifications to the framework behind the tests because a number of things were not supported that were necessary for the test case (i.e. getting timestamps from the feed, deleting packages using nuget.exe).