public async Task WhenExpectedOrderIsOldestFirst_ThenEventsAreProcessedInAscendingOrder(
            [Credential(Role = PredefinedRole.StorageObjectViewer)] ResourceTask <ICredential> credential)
        {
            var service = new AuditLogStorageSinkAdapter(
                new StorageAdapter(await credential),
                new AuditLogAdapter(await credential));

            var eventsProcessed = new List <EventBase>();
            var processor       = new Mock <IEventProcessor>();

            processor.SetupGet(p => p.ExpectedOrder).Returns(EventOrder.OldestFirst);
            processor.SetupGet(p => p.SupportedMethods).Returns(new[] { "NotifyInstanceLocation" });
            processor.SetupGet(p => p.SupportedSeverities).Returns(new[] { "INFO", "ERROR" });
            processor
            .Setup(p => p.Process(It.IsAny <EventBase>()))
            .Callback((EventBase e) => eventsProcessed.Add(e));

            var jan1 = new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc);

            await service.ProcessInstanceEventsAsync(
                GcsTestData.Bucket,
                jan1,
                jan1.AddMonths(1),
                processor.Object,
                CancellationToken.None);

            Assert.AreEqual(5, eventsProcessed.Count);

            Assert.AreEqual(EventTimestamp_Jan1_00_01, eventsProcessed[0].Timestamp);
            Assert.AreEqual(EventTimestamp_Jan1_00_02, eventsProcessed[1].Timestamp);
            Assert.AreEqual(EventTimestamp_Jan1_00_03, eventsProcessed[2].Timestamp);
            Assert.AreEqual(EventTimestamp_Jan1_01_01, eventsProcessed[3].Timestamp);
            Assert.AreEqual(EventTimestamp_Jan2_00_01, eventsProcessed[4].Timestamp);
        }
        public async Task WhenMethodDoesNotMatch_ThenEventsAreNotProcessed(
            [Credential(Role = PredefinedRole.StorageObjectViewer)] ResourceTask <ICredential> credential)
        {
            var service = new AuditLogStorageSinkAdapter(
                new StorageAdapter(await credential),
                new AuditLogAdapter(await credential));

            var eventsProcessed = new List <EventBase>();
            var processor       = new Mock <IEventProcessor>();

            processor.SetupGet(p => p.ExpectedOrder).Returns(EventOrder.OldestFirst);
            processor.SetupGet(p => p.SupportedMethods).Returns(new[] { "SomeOtherMethod" });
            processor.SetupGet(p => p.SupportedSeverities).Returns(new[] { "INFO" });
            processor
            .Setup(p => p.Process(It.IsAny <EventBase>()))
            .Callback((EventBase e) => eventsProcessed.Add(e));

            var jan1 = new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc);

            await service.ProcessInstanceEventsAsync(
                GcsTestData.Bucket,
                jan1,
                jan1.AddMonths(1),
                processor.Object,
                CancellationToken.None);

            Assert.AreEqual(0, eventsProcessed.Count);
        }
        public async Task WhenSinkTooYoung_ThenFindCloudStorageExportBucketForAuditLogsAsyncReturnsNull()
        {
            var auditLogAdapter = new Mock <IAuditLogAdapter>();

            auditLogAdapter.Setup(
                a => a.ListCloudStorageSinksAsync(
                    It.IsAny <string>(),
                    It.IsAny <CancellationToken>()))
            .ReturnsAsync(new[]
            {
                new LogSink()
                {
                    Destination = "storage.googleapis.com/mybucket",
                    CreateTime  = "2020-01-01"
                }
            });

            var service = new AuditLogStorageSinkAdapter(
                new Mock <IStorageAdapter>().Object,
                auditLogAdapter.Object);

            var bucket = await service.FindCloudStorageExportBucketForAuditLogsAsync(
                TestProject.ProjectId,
                new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc), // Before sink creation
                CancellationToken.None);

            Assert.IsNull(bucket);
        }
        public async Task WhenObjectContainsGarbage_ThenListInstanceEventsAsyncThrowsException(
            [Credential(Role = PredefinedRole.StorageObjectViewer)] ResourceTask <ICredential> credential)
        {
            var service = new AuditLogStorageSinkAdapter(
                new StorageAdapter(await credential),
                new AuditLogAdapter(await credential));

            AssertEx.ThrowsAggregateException <JsonReaderException>(
                () => service.ListInstanceEventsAsync(GarbageLocator, CancellationToken.None).Wait());
        }
        public async Task WhenObjectsContainEventExports_ThenListInstanceEventsAsyncReturnsEvents(
            [Credential(Role = PredefinedRole.StorageObjectViewer)] ResourceTask <ICredential> credential)
        {
            var service = new AuditLogStorageSinkAdapter(
                new StorageAdapter(await credential),
                new AuditLogAdapter(await credential));

            var events = await service.ListInstanceEventsAsync(
                new[] { EmptyLocator, ValidLocator_Jan1_00 },
                CancellationToken.None);

            Assert.AreEqual(3, events.Count());
        }
        public async Task WhenObjectIsEmpty_ThenListInstanceEventsAsyncReturnsEmptyListOfEvents(
            [Credential(Role = PredefinedRole.StorageObjectViewer)] ResourceTask <ICredential> credential)
        {
            var service = new AuditLogStorageSinkAdapter(
                new StorageAdapter(await credential),
                new AuditLogAdapter(await credential));

            var events = await service.ListInstanceEventsAsync(
                EmptyLocator,
                CancellationToken.None);

            CollectionAssert.IsEmpty(events);
        }
        public async Task WhenEndTimeBeforeStartTime_ThenFindExportObjectsThrowsException(
            [Credential(Role = PredefinedRole.StorageObjectViewer)] ResourceTask <ICredential> credential)
        {
            var service = new AuditLogStorageSinkAdapter(
                new StorageAdapter(await credential),
                new AuditLogAdapter(await credential));

            AssertEx.ThrowsAggregateException <ArgumentException>(
                () => service.FindAuditLogExportObjectsGroupedByDay(
                    GcsTestData.Bucket,
                    new DateTime(2020, 1, 2, 0, 0, 0, DateTimeKind.Utc),
                    new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc),
                    CancellationToken.None).Wait());
        }
        public async Task WhenUserIsInLogsViewerRoleOnly_ThenFindCloudStorageExportBucketForAuditLogsAsyncReturnsNull(
            [Credential(Role = PredefinedRole.LogsViewer)] ResourceTask <ICredential> credential)
        {
            var service = new AuditLogStorageSinkAdapter(
                new StorageAdapter(await credential),
                new AuditLogAdapter(await credential));

            var bucket = await service.FindCloudStorageExportBucketForAuditLogsAsync(
                TestProject.ProjectId,
                DateTime.Now.AddDays(-1),
                CancellationToken.None);

            Assert.IsNull(bucket, "Bucket not accessible, if if it existed");
        }
        public async Task WhenNoObjectsInRange_ThenFindExportObjectsReturnsEmptyDictionary(
            [Credential(Role = PredefinedRole.StorageObjectViewer)] ResourceTask <ICredential> credential)
        {
            var service = new AuditLogStorageSinkAdapter(
                new StorageAdapter(await credential),
                new AuditLogAdapter(await credential));

            var locators = await service.FindAuditLogExportObjectsGroupedByDay(
                GcsTestData.Bucket,
                new DateTime(2020, 2, 1, 0, 0, 0, DateTimeKind.Utc),
                new DateTime(2020, 2, 2, 0, 0, 0, DateTimeKind.Utc),
                CancellationToken.None);

            CollectionAssert.IsEmpty(locators);
        }
        public async Task WhenObjectsInRange_ThenFindExportObjectsReturnsList(
            [Credential(Role = PredefinedRole.StorageObjectViewer)] ResourceTask <ICredential> credential)
        {
            var service = new AuditLogStorageSinkAdapter(
                new StorageAdapter(await credential),
                new AuditLogAdapter(await credential));

            var jan1 = new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc);
            var jan2 = new DateTime(2020, 1, 2, 0, 0, 0, DateTimeKind.Utc);

            var locators = await service.FindAuditLogExportObjectsGroupedByDay(
                GcsTestData.Bucket,
                jan1,
                jan2,
                CancellationToken.None);

            Assert.AreEqual(2, locators.Count()); // 2 days
            Assert.AreEqual(2, locators[jan1].Count());
            Assert.AreEqual(1, locators[jan2].Count());
        }
        public async Task WhenExportBucketAccessDenied_ThenFindCloudStorageExportBucketForAuditLogsAsyncReturnsNull()
        {
            var auditLogAdapter = new Mock <IAuditLogAdapter>();

            auditLogAdapter.Setup(
                a => a.ListCloudStorageSinksAsync(
                    It.IsAny <string>(),
                    It.IsAny <CancellationToken>()))
            .ReturnsAsync(new[]
            {
                new LogSink()
                {
                    Destination = "storage.googleapis.com/mybucket",
                    CreateTime  = "2019-01-01"
                }
            });

            var storageAdapter = new Mock <IStorageAdapter>();

            storageAdapter.Setup(
                a => a.ListObjectsAsync(
                    It.Is <string>(b => b == "mybucket"),
                    It.Is <string>(p => p == AuditLogStorageSinkAdapter.AuditLogPrefix),
                    It.IsAny <CancellationToken>()))
            .ThrowsAsync(new ResourceAccessDeniedException("denied", null));

            var service = new AuditLogStorageSinkAdapter(
                storageAdapter.Object,
                auditLogAdapter.Object);

            var bucket = await service.FindCloudStorageExportBucketForAuditLogsAsync(
                TestProject.ProjectId,
                new DateTime(2020, 1, 1, 0, 0, 0, DateTimeKind.Utc),
                CancellationToken.None);

            Assert.IsNull(bucket);
        }