Skip to content
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

Merged
merged 10 commits into from
Dec 8, 2016

Conversation

scottbommarito
Copy link
Contributor

Idea behind the test case is this:

  1. Upload x packages synchronously (wait for each request to finish before starting the next one).
  2. Verify that the Created timestamps of the packages in the feed are in the correct order.
  3. Unlist the packages synchronously (wait for each request to finish before starting the next one).
  4. Verify that the 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).

/// 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")
Copy link
Contributor

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")
Copy link
Contributor

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.

Copy link
Contributor Author

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);
Copy link
Contributor

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?

Copy link
Contributor Author

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")
Copy link
Contributor

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).

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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;
Copy link
Contributor

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

Copy link
Contributor Author

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;
Copy link
Contributor

Choose a reason for hiding this comment

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

same

Copy link
Contributor Author

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?

Copy link
Contributor

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))
{
Copy link
Contributor

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

Copy link
Contributor Author

@scottbommarito scottbommarito Dec 6, 2016

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)
Copy link
Contributor

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;
Copy link
Contributor

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?

Copy link
Contributor Author

@scottbommarito scottbommarito Dec 6, 2016

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.

@scottbommarito
Copy link
Contributor Author

Ran this against dev with the fix implemented, works perfectly!

{
if (string.IsNullOrEmpty(packageId))
{
packageId = DateTime.Now.Ticks.ToString();
Copy link
Contributor

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?

Copy link
Contributor Author

@scottbommarito scottbommarito Dec 7, 2016

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);
}
Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I refactored this code from here and here because I needed the functionality in a function I wrote myself and didn't want to have the code in multiple places. I'll check to see if the tests run fine without it and remove it.

@@ -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)
{
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

@shishirx34 shishirx34 left a 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;
Copy link
Contributor

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);
Copy link
Contributor

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\">";
Copy link
Contributor

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;
Copy link
Contributor

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);
Copy link
Contributor

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);
Copy link
Contributor

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: " +
Copy link
Contributor

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}).");
Copy link
Contributor

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?

Copy link
Contributor Author

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.

@scottbommarito
Copy link
Contributor Author

@shishirx34 I have added an explicit comment to the top of the test case in honor of the discussion we had offline.

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.

5 participants