public static async Task MetadataApi_GetAvailablePackagesAsync_Throws_MetadataApiException_If_Error_Occurs()
        {
            // Arrange
            Uri         serviceUri = new Uri("https://ws.updates.qas.com/metadata/V1/");
            MetadataApi target     = new MetadataApi(serviceUri);

            // Act and Assert - Should throw as no credentials configured
            await Assert.ThrowsAsync <MetadataApiException>(() => target.GetAvailablePackagesAsync());
        }
        public static async Task MetadataApi_GetDownloadUriAsync_Returns_File_Download_Uri_Which_Downloads_Correct_File()
        {
            // Arrange
            MetadataApi target = CreateAuthorizedService();

            AvailablePackagesReply packages = await target.GetAvailablePackagesAsync();

            DataFile dataFile = packages.PackageGroups
                                .SelectMany((p) => p.Packages)
                                .First()
                                .Files
                                .First();

            // Act
            Uri result = await target.GetDownloadUriAsync(dataFile.FileName, dataFile.MD5Hash);

            // Assert
            Assert.NotNull(result);
            Assert.True(result.IsAbsoluteUri);

            string tempPath = Path.Combine(Path.GetTempPath(), dataFile.FileName);

            try
            {
                using (WebClient client = new WebClient())
                {
                    await client.DownloadFileTaskAsync(result, tempPath);
                }

                Assert.True(File.Exists(tempPath));

                byte[] hash;

                using (Stream stream = File.OpenRead(tempPath))
                {
                    Assert.Equal(dataFile.Size, stream.Length);

                    using (HashAlgorithm algorithm = HashAlgorithm.Create("MD5"))
                    {
                        hash = algorithm.ComputeHash(stream);
                    }
                }

                string hashString = string.Concat(hash.Select((p) => p.ToString("x2", CultureInfo.InvariantCulture)));

                Assert.Equal(dataFile.MD5Hash, hashString);
            }
            finally
            {
                if (File.Exists(tempPath))
                {
                    File.Delete(tempPath);
                }
            }
        }
        public static async Task MetadataApi_GetAvailablePackagesAsync_Returns_Available_Packages()
        {
            // Arrange
            MetadataApi target = CreateAuthorizedService();

            // Act
            AvailablePackagesReply result = await target.GetAvailablePackagesAsync();

            // Assert
            Assert.NotNull(result);
            Assert.NotNull(result.PackageGroups);
            Assert.NotEqual(0, result.PackageGroups.Count);
            Assert.DoesNotContain(null, result.PackageGroups);

            Assert.All(
                result.PackageGroups,
                (group) =>
            {
                Assert.False(string.IsNullOrEmpty(group.PackageGroupCode));
                Assert.False(string.IsNullOrEmpty(group.Vintage));

                Assert.NotNull(group.Packages);
                Assert.NotEqual(0, group.Packages.Count);
                Assert.DoesNotContain(null, group.Packages);

                Assert.All(
                    group.Packages,
                    (package) =>
                {
                    Assert.False(string.IsNullOrEmpty(package.PackageCode));

                    Assert.NotNull(package.Files);
                    Assert.NotEqual(0, package.Files.Count);
                    Assert.DoesNotContain(null, package.Files);

                    Assert.All(
                        package.Files,
                        (file) =>
                    {
                        Assert.False(string.IsNullOrEmpty(file.FileName));
                        Assert.False(string.IsNullOrEmpty(file.MD5Hash));
                        Assert.True(file.Size > 0L);
                    });
                });
            });
        }