public async Task CanRetrieveAndCacheUserAndAccountsAsync() { var orgs = new[] { CreateOctokitOrganization("github"), CreateOctokitOrganization("fake") }; var apiClient = Substitute.For <IApiClient>(); apiClient.GetUser().Returns(Observable.Return(CreateOctokitUser("snoopy"))); apiClient.GetOrganizations().Returns(orgs.ToObservable()); var cache = new InMemoryBlobCache(); var modelService = CreateTarget(apiClient: apiClient, hostCache: cache); await modelService.InsertUser(new AccountCacheItem { Login = "******" }); var fetched = await modelService.GetAccounts(); Assert.That(3, Is.EqualTo(fetched.Count)); Assert.That("snoopy", Is.EqualTo(fetched[0].Login)); Assert.That("github", Is.EqualTo(fetched[1].Login)); Assert.That("fake", Is.EqualTo(fetched[2].Login)); var cachedOrgs = await cache.GetObject <IReadOnlyList <AccountCacheItem> >("snoopy|orgs"); Assert.That(2, Is.EqualTo(cachedOrgs.Count)); Assert.That("github", Is.EqualTo(cachedOrgs[0].Login)); Assert.That("fake", Is.EqualTo(cachedOrgs[1].Login)); var cachedUser = await cache.GetObject <AccountCacheItem>("user"); Assert.That("snoopy", Is.EqualTo(cachedUser.Login)); }
public async Task CanRetrieveUserFromCacheAndAccountsFromApi() { var orgs = new[] { CreateOctokitOrganization("github"), CreateOctokitOrganization("fake") }; var apiClient = Substitute.For <IApiClient>(); apiClient.GetOrganizations().Returns(orgs.ToObservable()); var cache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, cache, Substitute.For <IAvatarProvider>()); await modelService.InsertUser(new AccountCacheItem(CreateOctokitUser("octocat"))); var fetched = await modelService.GetAccounts(); Assert.Equal(3, fetched.Count); Assert.Equal("octocat", fetched[0].Login); Assert.Equal("github", fetched[1].Login); Assert.Equal("fake", fetched[2].Login); var cachedOrgs = await cache.GetObject <IReadOnlyList <AccountCacheItem> >("octocat|orgs"); Assert.Equal(2, cachedOrgs.Count); Assert.Equal("github", cachedOrgs[0].Login); Assert.Equal("fake", cachedOrgs[1].Login); var cachedUser = await cache.GetObject <AccountCacheItem>("user"); Assert.Equal("octocat", cachedUser.Login); }
public async Task CanRetrieveAndCacheLicenses() { var data = new[] { new LicenseMetadata("mit", "MIT", new Uri("https://github.com/")), new LicenseMetadata("apache", "Apache", new Uri("https://github.com/")) }; var apiClient = Substitute.For <IApiClient>(); apiClient.GetLicenses().Returns(data.ToObservable()); var cache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, cache, Substitute.For <IAvatarProvider>()); var fetched = await modelService.GetLicenses().ToList(); Assert.Equal(2, fetched.Count); for (int i = 0; i < data.Length; i++) { Assert.Equal(data[i].Name, fetched[i].Name); } var indexKey = CacheIndex.LicensesPrefix; var cached = await cache.GetObject <CacheIndex>(indexKey); Assert.Equal(2, cached.Keys.Count); var items = await cache.GetObjects <LicenseCacheItem>(cached.Keys).Take(1); for (int i = 0; i < data.Length; i++) { Assert.Equal(data[i].Name, items[indexKey + "|" + data[i].Key].Name); } }
public async Task CanRetrieveAndCacheLicensesAsync() { var data = new[] { new LicenseMetadata("mit", null, "MIT", "foo", "https://github.com/", false), new LicenseMetadata("apache", null, "Apache", "foo", "https://github.com/", false) }; var apiClient = Substitute.For <IApiClient>(); apiClient.GetLicenses().Returns(data.ToObservable()); var cache = new InMemoryBlobCache(); var modelService = CreateTarget(apiClient: apiClient, hostCache: cache); var fetched = await modelService.GetLicenses().ToList(); Assert.That(2, Is.EqualTo(fetched.Count)); for (int i = 0; i < data.Length; i++) { Assert.That(data[i].Name, Is.EqualTo(fetched[i].Name)); } var indexKey = CacheIndex.LicensesPrefix; var cached = await cache.GetObject <CacheIndex>(indexKey); Assert.That(2, Is.EqualTo(cached.Keys.Count)); var items = await cache.GetObjects <LicenseCacheItem>(cached.Keys).Take(1); for (int i = 0; i < data.Length; i++) { Assert.That(data[i].Name, Is.EqualTo(items[indexKey + "|" + data[i].Key].Name)); } }
public async Task CanRetrieveAndCacheLicenses() { var licenses = new[] { new LicenseMetadata("mit", "MIT", new Uri("https://github.com/")), new LicenseMetadata("apache", "Apache", new Uri("https://github.com/")) }; var apiClient = Substitute.For <IApiClient>(); apiClient.GetLicenses().Returns(licenses.ToObservable()); var cache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, cache, Substitute.For <IAvatarProvider>()); var fetched = await modelService.GetLicenses(); Assert.Equal(3, fetched.Count); Assert.Equal("None", fetched[0].Name); Assert.Equal("MIT", fetched[1].Name); Assert.Equal("Apache", fetched[2].Name); var cached = await cache.GetObject <IReadOnlyList <ModelService.LicenseCacheItem> >("licenses"); Assert.Equal(2, cached.Count); Assert.Equal("mit", cached[0].Key); Assert.Equal("apache", cached[1].Key); }
public async Task DoesNotLogInWhenRetrievingOauthTokenFails() { var apiClient = Substitute.For <IApiClient>(); apiClient.HostAddress.Returns(HostAddress.GitHubDotComHostAddress); apiClient.GetOrCreateApplicationAuthenticationCode( Args.TwoFactorChallengCallback, Args.String, Args.Boolean) .Returns(Observable.Throw <ApplicationAuthorization>(new NotFoundException("", HttpStatusCode.BadGateway))); apiClient.GetUser().Returns(Observable.Return(CreateUserAndScopes("jiminy"))); var hostCache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, hostCache, Substitute.For <IAvatarProvider>()); var loginCache = new TestLoginCache(); var host = new RepositoryHost(apiClient, modelService, loginCache, Substitute.For <ITwoFactorChallengeHandler>()); await Assert.ThrowsAsync <NotFoundException>(async() => await host.LogIn("jiminy", "cricket")); await Assert.ThrowsAsync <KeyNotFoundException>( async() => await hostCache.GetObject <AccountCacheItem>("user")); var loginInfo = await loginCache.GetLoginAsync(HostAddress.GitHubDotComHostAddress); Assert.Equal("jiminy", loginInfo.UserName); Assert.Equal("cricket", loginInfo.Password); Assert.False(host.IsLoggedIn); }
public async Task LogsTheUserInSuccessfullyAndCachesRelevantInfo() { var apiClient = Substitute.For <IApiClient>(); apiClient.HostAddress.Returns(HostAddress.GitHubDotComHostAddress); apiClient.GetOrCreateApplicationAuthenticationCode( Args.TwoFactorChallengCallback, Args.String, Args.Boolean) .Returns(Observable.Return(new ApplicationAuthorization("S3CR3TS"))); apiClient.GetUser().Returns(Observable.Return(CreateUserAndScopes("baymax"))); var hostCache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, hostCache, Substitute.For <IAvatarProvider>()); var loginCache = new TestLoginCache(); var host = new RepositoryHost(apiClient, modelService, loginCache, Substitute.For <ITwoFactorChallengeHandler>()); var result = await host.LogIn("baymax", "aPassword"); Assert.Equal(AuthenticationResult.Success, result); var user = await hostCache.GetObject <AccountCacheItem>("user"); Assert.NotNull(user); Assert.Equal("baymax", user.Login); var loginInfo = await loginCache.GetLoginAsync(HostAddress.GitHubDotComHostAddress); Assert.Equal("baymax", loginInfo.UserName); Assert.Equal("S3CR3TS", loginInfo.Password); Assert.True(host.IsLoggedIn); }
public async Task LogsTheUserInSuccessfullyAndCachesRelevantInfo() { var apiClient = Substitute.For<IApiClient>(); apiClient.HostAddress.Returns(HostAddress.GitHubDotComHostAddress); apiClient.GetOrCreateApplicationAuthenticationCode( Args.TwoFactorChallengCallback, Args.String, Args.Boolean) .Returns(Observable.Return(new ApplicationAuthorization("S3CR3TS"))); apiClient.GetUser().Returns(Observable.Return(CreateUserAndScopes("baymax"))); var hostCache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, hostCache, Substitute.For<IAvatarProvider>()); var loginCache = new TestLoginCache(); var usage = Substitute.For<IUsageTracker>(); var host = new RepositoryHost(apiClient, modelService, loginCache, Substitute.For<ITwoFactorChallengeHandler>(), usage); var result = await host.LogIn("baymax", "aPassword"); Assert.Equal(AuthenticationResult.Success, result); var user = await hostCache.GetObject<AccountCacheItem>("user"); Assert.NotNull(user); Assert.Equal("baymax", user.Login); var loginInfo = await loginCache.GetLoginAsync(HostAddress.GitHubDotComHostAddress); Assert.Equal("baymax", loginInfo.UserName); Assert.Equal("S3CR3TS", loginInfo.Password); Assert.True(host.IsLoggedIn); }
public async Task CanRetrieveAndCacheGitIgnoresAsync() { var data = new[] { "dotnet", "peanuts", "bloomcounty" }; var apiClient = Substitute.For <IApiClient>(); apiClient.GetGitIgnoreTemplates().Returns(data.ToObservable()); var cache = new InMemoryBlobCache(); var modelService = CreateTarget(apiClient: apiClient, hostCache: cache); var fetched = await modelService.GetGitIgnoreTemplates().ToList(); Assert.That(3, Is.EqualTo(fetched.Count)); for (int i = 0; i < data.Length; i++) { Assert.That(data[i], Is.EqualTo(fetched[i].Name)); } var indexKey = CacheIndex.GitIgnoresPrefix; var cached = await cache.GetObject <CacheIndex>(indexKey); Assert.That(3, Is.EqualTo(cached.Keys.Count)); var items = await cache.GetObjects <GitIgnoreCacheItem>(cached.Keys).Take(1); for (int i = 0; i < data.Length; i++) { Assert.That(data[i], Is.EqualTo(items[indexKey + "|" + data[i]].Name)); } }
public async Task AddsUserToCacheAsync() { var cache = new InMemoryBlobCache(); var modelService = CreateTarget(hostCache: cache); var user = await modelService.InsertUser(new AccountCacheItem(CreateOctokitUser("octocat"))); var cached = await cache.GetObject <AccountCacheItem>("user"); Assert.That("octocat", Is.EqualTo(cached.Login)); }
public async Task AddsUserToCache() { var apiClient = Substitute.For<IApiClient>(); var cache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, cache, Substitute.For<IAvatarProvider>()); var user = await modelService.InsertUser(new AccountCacheItem(CreateOctokitUser("octocat"))); var cached = await cache.GetObject<AccountCacheItem>("user"); Assert.Equal("octocat", cached.Login); }
public async Task AddsUserToCache() { var apiClient = Substitute.For <IApiClient>(); var cache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, cache, Substitute.For <IAvatarProvider>()); var user = await modelService.InsertUser(new AccountCacheItem(CreateOctokitUser("octocat"))); var cached = await cache.GetObject <AccountCacheItem>("user"); Assert.Equal("octocat", cached.Login); }
public async Task NotFoundRequestIsMarked() { var fetcher = Substitute.For<IArtworkFetcher>(); fetcher.RetrieveAsync(Arg.Any<string>(), Arg.Any<string>()).Returns(Task.FromResult<Uri>(null)); var blobCache = new InMemoryBlobCache(); var fixture = new ArtworkCache(blobCache, fetcher); string artist = "A"; string album = "B"; string lookupKey = BlobCacheKeys.GetKeyForOnlineArtwork(artist, album); await fixture.FetchOnline(artist, album); Assert.Equal("FAILED", await blobCache.GetObject<string>(lookupKey)); }
public async Task NotFoundRequestIsMarked() { var fetcher = Substitute.For <IArtworkFetcher>(); fetcher.RetrieveAsync(Arg.Any <string>(), Arg.Any <string>()).Returns(Task.FromResult <Uri>(null)); var blobCache = new InMemoryBlobCache(); var fixture = new ArtworkCache(blobCache, fetcher); string artist = "A"; string album = "B"; string lookupKey = BlobCacheKeys.GetKeyForOnlineArtwork(artist, album); await fixture.FetchOnline(artist, album); Assert.Equal("FAILED", await blobCache.GetObject <string>(lookupKey)); }
public async Task DoesNotLogInWhenRetrievingOauthTokenFails() { var apiClient = Substitute.For <IApiClient>(); apiClient.HostAddress.Returns(HostAddress.GitHubDotComHostAddress); var hostCache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, hostCache, Substitute.For <IAvatarProvider>()); var loginManager = Substitute.For <ILoginManager>(); loginManager.Login(HostAddress.GitHubDotComHostAddress, Arg.Any <IGitHubClient>(), "jiminy", "cricket") .Returns <User>(_ => { throw new NotFoundException("", HttpStatusCode.BadGateway); }); var loginCache = new TestLoginCache(); var usage = Substitute.For <IUsageTracker>(); var host = new RepositoryHost(apiClient, modelService, loginManager, loginCache, usage); await Assert.ThrowsAsync <NotFoundException>(async() => await host.LogIn("jiminy", "cricket")); await Assert.ThrowsAsync <KeyNotFoundException>( async() => await hostCache.GetObject <AccountCacheItem>("user")); }
public async Task CanRetrieveAndCacheGitIgnores() { var templates = new[] { "dotnet", "peanuts", "bloomcounty" }; var apiClient = Substitute.For<IApiClient>(); apiClient.GetGitIgnoreTemplates().Returns(templates.ToObservable()); var cache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, cache, Substitute.For<IAvatarProvider>()); var fetched = await modelService.GetGitIgnoreTemplates(); Assert.Equal(4, fetched.Count); Assert.Equal("None", fetched[0].Name); Assert.Equal("dotnet", fetched[1].Name); Assert.Equal("peanuts", fetched[2].Name); Assert.Equal("bloomcounty", fetched[3].Name); var cached = await cache.GetObject<IReadOnlyList<string>>("gitignores"); Assert.Equal(3, cached.Count); Assert.Equal("dotnet", cached[0]); Assert.Equal("peanuts", cached[1]); Assert.Equal("bloomcounty", cached[2]); }
public async Task CanRetrieveAndCacheGitIgnores() { var data = new[] { "dotnet", "peanuts", "bloomcounty" }; var apiClient = Substitute.For<IApiClient>(); apiClient.GetGitIgnoreTemplates().Returns(data.ToObservable()); var cache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, cache, Substitute.For<IAvatarProvider>()); var fetched = await modelService.GetGitIgnoreTemplates().ToList(); Assert.Equal(3, fetched.Count); for (int i = 0; i < data.Length; i++) Assert.Equal(data[i], fetched[i].Name); var indexKey = CacheIndex.GitIgnoresPrefix; var cached = await cache.GetObject<CacheIndex>(indexKey); Assert.Equal(3, cached.Keys.Count); var items = await cache.GetObjects<GitIgnoreCacheItem>(cached.Keys).Take(1); for (int i = 0; i < data.Length; i++) Assert.Equal(data[i], items[indexKey + "|" + data[i]].Name); }
public async Task LogsTheUserInSuccessfullyAndCachesRelevantInfo() { var apiClient = Substitute.For <IApiClient>(); apiClient.HostAddress.Returns(HostAddress.GitHubDotComHostAddress); var hostCache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, hostCache, Substitute.For <IAvatarProvider>()); var loginManager = Substitute.For <ILoginManager>(); loginManager.LoginFromCache(HostAddress.GitHubDotComHostAddress, Arg.Any <IGitHubClient>()).Returns(CreateOctokitUser("baymax")); var loginCache = new TestLoginCache(); var usage = Substitute.For <IUsageTracker>(); var host = new RepositoryHost(apiClient, modelService, loginManager, loginCache, usage); var result = await host.LogInFromCache(); Assert.Equal(AuthenticationResult.Success, result); var user = await hostCache.GetObject <AccountCacheItem>("user"); Assert.NotNull(user); Assert.Equal("baymax", user.Login); }
public async Task DoesNotLogInWhenRetrievingOauthTokenFails() { var apiClient = Substitute.For<IApiClient>(); apiClient.HostAddress.Returns(HostAddress.GitHubDotComHostAddress); apiClient.GetOrCreateApplicationAuthenticationCode( Args.TwoFactorChallengCallback, Args.String, Args.Boolean) .Returns(Observable.Throw<ApplicationAuthorization>(new NotFoundException("", HttpStatusCode.BadGateway))); apiClient.GetUser().Returns(Observable.Return(CreateOctokitUser("jiminy"))); var hostCache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, hostCache, Substitute.For<IAvatarProvider>()); var loginCache = new TestLoginCache(); var host = new RepositoryHost(apiClient, modelService, loginCache, Substitute.For<ITwoFactorChallengeHandler>()); await Assert.ThrowsAsync<NotFoundException>(async () => await host.LogIn("jiminy", "cricket")); await Assert.ThrowsAsync<KeyNotFoundException>( async () => await hostCache.GetObject<AccountCacheItem>("user")); var loginInfo = await loginCache.GetLoginAsync(HostAddress.GitHubDotComHostAddress); Assert.Equal("jiminy", loginInfo.UserName); Assert.Equal("cricket", loginInfo.Password); Assert.False(host.IsLoggedIn); }
public async Task CanRetrieveAndCacheGitIgnores() { var templates = new[] { "dotnet", "peanuts", "bloomcounty" }; var apiClient = Substitute.For <IApiClient>(); apiClient.GetGitIgnoreTemplates().Returns(templates.ToObservable()); var cache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, cache, Substitute.For <IAvatarProvider>()); var fetched = await modelService.GetGitIgnoreTemplates(); Assert.Equal(4, fetched.Count); Assert.Equal("None", fetched[0].Name); Assert.Equal("dotnet", fetched[1].Name); Assert.Equal("peanuts", fetched[2].Name); Assert.Equal("bloomcounty", fetched[3].Name); var cached = await cache.GetObject <IReadOnlyList <string> >("gitignores"); Assert.Equal(3, cached.Count); Assert.Equal("dotnet", cached[0]); Assert.Equal("peanuts", cached[1]); Assert.Equal("bloomcounty", cached[2]); }
public async Task ExpiredIndexClearsItemsAsync() { var expected = 5; var username = "******"; var reponame = "repo"; var cache = new InMemoryBlobCache(); var apiClient = Substitute.For <IApiClient>(); var modelService = CreateTarget(apiClient: apiClient, hostCache: cache); var user = CreateOctokitUser(username); apiClient.GetUser().Returns(Observable.Return(user)); apiClient.GetOrganizations().Returns(Observable.Empty <Organization>()); var act = modelService.GetAccounts().ToEnumerable().First().First(); var repo = Substitute.For <LocalRepositoryModel>(); repo.Name.Returns(reponame); repo.Owner.Returns(user.Login); repo.CloneUrl.Returns(new UriString("https://github.com/" + username + "/" + reponame)); var indexKey = string.Format(CultureInfo.InvariantCulture, "{0}|{1}:{2}", CacheIndex.PRPrefix, user.Login, repo.Name); var prcache = Enumerable.Range(1, expected) .Select(id => CreatePullRequest(user, id, ItemState.Open, "Cache " + id, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow)); // seed the cache prcache .Select(item => new ModelService.PullRequestCacheItem(item)) .Select(item => item.Save <ModelService.PullRequestCacheItem>(cache, indexKey).ToEnumerable().First()) .SelectMany(item => CacheIndex.AddAndSaveToIndex(cache, indexKey, item).ToEnumerable()) .ToList(); // expire the index var indexobj = await cache.GetObject <CacheIndex>(indexKey); indexobj.UpdatedAt = DateTimeOffset.UtcNow - TimeSpan.FromMinutes(6); await cache.InsertObject(indexKey, indexobj); var prlive = Observable.Range(5, expected) .Select(id => CreatePullRequest(user, id, ItemState.Open, "Live " + id, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, 0)) .DelaySubscription(TimeSpan.FromMilliseconds(10)); apiClient.GetPullRequestsForRepository(user.Login, repo.Name).Returns(prlive); await modelService.InsertUser(new AccountCacheItem(user)); ITrackingCollection <IPullRequestModel> col = new TrackingCollection <IPullRequestModel>(); modelService.GetPullRequests(repo, col); col.ProcessingDelay = TimeSpan.Zero; var count = 0; var done = new ReplaySubject <Unit>(); done.OnNext(Unit.Default); done.Subscribe(); col.Subscribe(t => { // we get all the items from the cache (items 1-5), all the items from the live (items 5-9), // and 4 deletions (items 1-4) because the cache expired the items that were not // a part of the live data if (++count == 14) { done.OnCompleted(); } }, () => { }); await done; Assert.That(5, Is.EqualTo(col.Count)); /**Assert.Collection(col, * t => { Assert.StartsWith("Live", t.Title); Assert.Equal(5, t.Number); }, * t => { Assert.StartsWith("Live", t.Title); Assert.Equal(6, t.Number); }, * t => { Assert.StartsWith("Live", t.Title); Assert.Equal(7, t.Number); }, * t => { Assert.StartsWith("Live", t.Title); Assert.Equal(8, t.Number); }, * t => { Assert.StartsWith("Live", t.Title); Assert.Equal(9, t.Number); } * );*/ }
public async Task ExpiredIndexClearsItems() { var expected = 5; var username = "******"; var reponame = "repo"; var cache = new InMemoryBlobCache(); var apiClient = Substitute.For<IApiClient>(); var modelService = new ModelService(apiClient, cache, Substitute.For<IAvatarProvider>()); var user = CreateOctokitUser(username); apiClient.GetUser().Returns(Observable.Return(user)); apiClient.GetOrganizations().Returns(Observable.Empty<Organization>()); var act = modelService.GetAccounts().ToEnumerable().First().First(); var repo = Substitute.For<ISimpleRepositoryModel>(); repo.Name.Returns(reponame); repo.CloneUrl.Returns(new UriString("https://github.com/" + username + "/" + reponame)); var indexKey = string.Format(CultureInfo.InvariantCulture, "{0}|{1}|pr", user.Login, repo.Name); var prcache = Enumerable.Range(1, expected) .Select(id => CreatePullRequest(user, id, ItemState.Open, "Cache " + id, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, 0)); // seed the cache prcache .Select(item => new ModelService.PullRequestCacheItem(item)) .Select(item => item.Save<ModelService.PullRequestCacheItem>(cache, indexKey).ToEnumerable().First()) .SelectMany(item => CacheIndex.AddAndSaveToIndex(cache, indexKey, item).ToEnumerable()) .ToList(); // expire the index var indexobj = await cache.GetObject<CacheIndex>(indexKey); indexobj.UpdatedAt = DateTimeOffset.UtcNow - TimeSpan.FromMinutes(6); await cache.InsertObject(indexKey, indexobj); var prlive = Observable.Range(5, expected) .Select(id => CreatePullRequest(user, id, ItemState.Open, "Live " + id, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, 0)) .DelaySubscription(TimeSpan.FromMilliseconds(10)); apiClient.GetPullRequestsForRepository(user.Login, repo.Name).Returns(prlive); await modelService.InsertUser(new AccountCacheItem(user)); var col = modelService.GetPullRequests(repo); col.ProcessingDelay = TimeSpan.Zero; var count = 0; var evt = new ManualResetEvent(false); col.Subscribe(t => { // we get all the items from the cache (items 1-5), all the items from the live (items 5-9), // and 4 deletions (items 1-4) because the cache expired the items that were not // a part of the live data if (++count == 14) evt.Set(); }, () => { }); evt.WaitOne(); evt.Reset(); Assert.Equal(5, col.Count); Assert.Collection(col, t => { Assert.True(t.Title.StartsWith("Live")); Assert.Equal(5, t.Number); }, t => { Assert.True(t.Title.StartsWith("Live")); Assert.Equal(6, t.Number); }, t => { Assert.True(t.Title.StartsWith("Live")); Assert.Equal(7, t.Number); }, t => { Assert.True(t.Title.StartsWith("Live")); Assert.Equal(8, t.Number); }, t => { Assert.True(t.Title.StartsWith("Live")); Assert.Equal(9, t.Number); } ); }
public async Task CanRetrieveAndCacheRepositoriesForUserAndOrganizations() { var orgs = new[] { CreateOctokitOrganization("github"), CreateOctokitOrganization("octokit") }; var ownedRepos = new[] { CreateRepository("haacked", "seegit"), CreateRepository("haacked", "codehaacks") }; var memberRepos = new[] { CreateRepository("mojombo", "semver"), CreateRepository("ninject", "ninject"), CreateRepository("jabbr", "jabbr"), CreateRepository("fody", "nullguard") }; var githubRepos = new[] { CreateRepository("github", "visualstudio") }; var octokitRepos = new[] { CreateRepository("octokit", "octokit.net"), CreateRepository("octokit", "octokit.rb"), CreateRepository("octokit", "octokit.objc") }; var apiClient = Substitute.For<IApiClient>(); apiClient.GetOrganizations().Returns(orgs.ToObservable()); apiClient.GetUserRepositories(RepositoryType.Owner).Returns(ownedRepos.ToObservable()); apiClient.GetUserRepositories(RepositoryType.Member).Returns(memberRepos.ToObservable()); apiClient.GetRepositoriesForOrganization("github").Returns(githubRepos.ToObservable()); apiClient.GetRepositoriesForOrganization("octokit").Returns(octokitRepos.ToObservable()); var cache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, cache, Substitute.For<IAvatarProvider>()); await modelService.InsertUser(new AccountCacheItem { Login = "******" }); var fetched = await modelService.GetRepositories().ToList(); Assert.Equal(4, fetched.Count); Assert.Equal(2, fetched[0].Count); Assert.Equal(4, fetched[1].Count); Assert.Equal(1, fetched[2].Count); Assert.Equal(3, fetched[3].Count); Assert.Equal("seegit", fetched[0][0].Name); Assert.Equal("codehaacks", fetched[0][1].Name); Assert.Equal("semver", fetched[1][0].Name); Assert.Equal("ninject", fetched[1][1].Name); Assert.Equal("jabbr", fetched[1][2].Name); Assert.Equal("nullguard", fetched[1][3].Name); Assert.Equal("visualstudio", fetched[2][0].Name); Assert.Equal("octokit.net", fetched[3][0].Name); Assert.Equal("octokit.rb", fetched[3][1].Name); Assert.Equal("octokit.objc", fetched[3][2].Name); var cachedOwnerRepositories = await cache.GetObject<IReadOnlyList<ModelService.RepositoryCacheItem>>("opus|Owner:repos"); Assert.Equal(2, cachedOwnerRepositories.Count); Assert.Equal("seegit", cachedOwnerRepositories[0].Name); Assert.Equal("haacked", cachedOwnerRepositories[0].Owner.Login); Assert.Equal("codehaacks", cachedOwnerRepositories[1].Name); Assert.Equal("haacked", cachedOwnerRepositories[1].Owner.Login); var cachedMemberRepositories = await cache.GetObject<IReadOnlyList<ModelService.RepositoryCacheItem>>("opus|Member:repos"); Assert.Equal(4, cachedMemberRepositories.Count); Assert.Equal("semver", cachedMemberRepositories[0].Name); Assert.Equal("mojombo", cachedMemberRepositories[0].Owner.Login); Assert.Equal("ninject", cachedMemberRepositories[1].Name); Assert.Equal("ninject", cachedMemberRepositories[1].Owner.Login); Assert.Equal("jabbr", cachedMemberRepositories[2].Name); Assert.Equal("jabbr", cachedMemberRepositories[2].Owner.Login); Assert.Equal("nullguard", cachedMemberRepositories[3].Name); Assert.Equal("fody", cachedMemberRepositories[3].Owner.Login); var cachedGitHubRepositories = await cache.GetObject<IReadOnlyList<ModelService.RepositoryCacheItem>>("opus|github|repos"); Assert.Equal(1, cachedGitHubRepositories.Count); Assert.Equal("seegit", cachedOwnerRepositories[0].Name); Assert.Equal("haacked", cachedOwnerRepositories[0].Owner.Login); Assert.Equal("codehaacks", cachedOwnerRepositories[1].Name); Assert.Equal("haacked", cachedOwnerRepositories[1].Owner.Login); var cachedOctokitRepositories = await cache.GetObject<IReadOnlyList<ModelService.RepositoryCacheItem>>("opus|octokit|repos"); Assert.Equal("octokit.net", cachedOctokitRepositories[0].Name); Assert.Equal("octokit", cachedOctokitRepositories[0].Owner.Login); Assert.Equal("octokit.rb", cachedOctokitRepositories[1].Name); Assert.Equal("octokit", cachedOctokitRepositories[1].Owner.Login); Assert.Equal("octokit.objc", cachedOctokitRepositories[2].Name); Assert.Equal("octokit", cachedOctokitRepositories[2].Owner.Login); }
public async Task CanRetrieveUserFromCacheAndAccountsFromApi() { var orgs = new[] { CreateOctokitOrganization("github"), CreateOctokitOrganization("fake") }; var apiClient = Substitute.For<IApiClient>(); apiClient.GetOrganizations().Returns(orgs.ToObservable()); var cache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, cache, Substitute.For<IAvatarProvider>()); await modelService.InsertUser(new AccountCacheItem(CreateOctokitUser("octocat"))); var fetched = await modelService.GetAccounts(); Assert.Equal(3, fetched.Count); Assert.Equal("octocat", fetched[0].Login); Assert.Equal("github", fetched[1].Login); Assert.Equal("fake", fetched[2].Login); var cachedOrgs = await cache.GetObject<IReadOnlyList<AccountCacheItem>>("octocat|orgs"); Assert.Equal(2, cachedOrgs.Count); Assert.Equal("github", cachedOrgs[0].Login); Assert.Equal("fake", cachedOrgs[1].Login); var cachedUser = await cache.GetObject<AccountCacheItem>("user"); Assert.Equal("octocat", cachedUser.Login); }
public async Task CanRetrieveAndCacheLicenses() { var licenses = new[] { new LicenseMetadata("mit", "MIT", new Uri("https://github.com/")), new LicenseMetadata("apache", "Apache", new Uri("https://github.com/")) }; var apiClient = Substitute.For<IApiClient>(); apiClient.GetLicenses().Returns(licenses.ToObservable()); var cache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, cache, Substitute.For<IAvatarProvider>()); var fetched = await modelService.GetLicenses(); Assert.Equal(3, fetched.Count); Assert.Equal("None", fetched[0].Name); Assert.Equal("MIT", fetched[1].Name); Assert.Equal("Apache", fetched[2].Name); var cached = await cache.GetObject<IReadOnlyList<ModelService.LicenseCacheItem>>("licenses"); Assert.Equal(2, cached.Count); Assert.Equal("mit", cached[0].Key); Assert.Equal("apache", cached[1].Key); }
public async Task ExpiredIndexClearsItems() { var expected = 5; var username = "******"; var reponame = "repo"; var cache = new InMemoryBlobCache(); var apiClient = Substitute.For <IApiClient>(); var modelService = new ModelService(apiClient, cache, Substitute.For <IAvatarProvider>()); var user = CreateOctokitUser(username); apiClient.GetUser().Returns(Observable.Return(new UserAndScopes(user, null))); apiClient.GetOrganizations().Returns(Observable.Empty <Organization>()); var act = modelService.GetAccounts().ToEnumerable().First().First(); var repo = Substitute.For <ISimpleRepositoryModel>(); repo.Name.Returns(reponame); repo.CloneUrl.Returns(new UriString("https://github.com/" + username + "/" + reponame)); var indexKey = string.Format(CultureInfo.InvariantCulture, "{0}|{1}|pr", user.Login, repo.Name); var prcache = Enumerable.Range(1, expected) .Select(id => CreatePullRequest(user, id, ItemState.Open, "Cache " + id, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, 0)); // seed the cache prcache .Select(item => new ModelService.PullRequestCacheItem(item)) .Select(item => item.Save <ModelService.PullRequestCacheItem>(cache, indexKey).ToEnumerable().First()) .SelectMany(item => CacheIndex.AddAndSaveToIndex(cache, indexKey, item).ToEnumerable()) .ToList(); // expire the index var indexobj = await cache.GetObject <CacheIndex>(indexKey); indexobj.UpdatedAt = DateTimeOffset.UtcNow - TimeSpan.FromMinutes(6); await cache.InsertObject(indexKey, indexobj); var prlive = Observable.Range(5, expected) .Select(id => CreatePullRequest(user, id, ItemState.Open, "Live " + id, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow, 0)) .DelaySubscription(TimeSpan.FromMilliseconds(10)); apiClient.GetPullRequestsForRepository(user.Login, repo.Name).Returns(prlive); await modelService.InsertUser(new AccountCacheItem(user)); var col = modelService.GetPullRequests(repo); col.ProcessingDelay = TimeSpan.Zero; var count = 0; var evt = new ManualResetEvent(false); col.Subscribe(t => { // we get all the items from the cache (items 1-5), all the items from the live (items 5-9), // and 4 deletions (items 1-4) because the cache expired the items that were not // a part of the live data if (++count == 14) { evt.Set(); } }, () => { }); evt.WaitOne(); evt.Reset(); Assert.Equal(5, col.Count); Assert.Collection(col, t => { Assert.True(t.Title.StartsWith("Live")); Assert.Equal(5, t.Number); }, t => { Assert.True(t.Title.StartsWith("Live")); Assert.Equal(6, t.Number); }, t => { Assert.True(t.Title.StartsWith("Live")); Assert.Equal(7, t.Number); }, t => { Assert.True(t.Title.StartsWith("Live")); Assert.Equal(8, t.Number); }, t => { Assert.True(t.Title.StartsWith("Live")); Assert.Equal(9, t.Number); } ); }
public async Task CanRetrieveAndCacheLicenses() { var data = new[] { new LicenseMetadata("mit", "MIT", new Uri("https://github.com/")), new LicenseMetadata("apache", "Apache", new Uri("https://github.com/")) }; var apiClient = Substitute.For<IApiClient>(); apiClient.GetLicenses().Returns(data.ToObservable()); var cache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, cache, Substitute.For<IAvatarProvider>()); var fetched = await modelService.GetLicenses().ToList(); Assert.Equal(2, fetched.Count); for (int i = 0; i < data.Length; i++) Assert.Equal(data[i].Name, fetched[i].Name); var indexKey = CacheIndex.LicensesPrefix; var cached = await cache.GetObject<CacheIndex>(indexKey); Assert.Equal(2, cached.Keys.Count); var items = await cache.GetObjects<LicenseCacheItem>(cached.Keys).Take(1); for (int i = 0; i < data.Length; i++) Assert.Equal(data[i].Name, items[indexKey + "|" + data[i].Key].Name); }
public async Task CanRetrieveAndCacheRepositoriesForUserAndOrganizations() { var orgs = new[] { CreateOctokitOrganization("github"), CreateOctokitOrganization("octokit") }; var ownedRepos = new[] { CreateRepository("haacked", "seegit"), CreateRepository("haacked", "codehaacks") }; var memberRepos = new[] { CreateRepository("mojombo", "semver"), CreateRepository("ninject", "ninject"), CreateRepository("jabbr", "jabbr"), CreateRepository("fody", "nullguard") }; var githubRepos = new[] { CreateRepository("github", "visualstudio") }; var octokitRepos = new[] { CreateRepository("octokit", "octokit.net"), CreateRepository("octokit", "octokit.rb"), CreateRepository("octokit", "octokit.objc") }; var apiClient = Substitute.For <IApiClient>(); apiClient.GetOrganizations().Returns(orgs.ToObservable()); apiClient.GetUserRepositories(RepositoryType.Owner).Returns(ownedRepos.ToObservable()); apiClient.GetUserRepositories(RepositoryType.Member).Returns(memberRepos.ToObservable()); apiClient.GetRepositoriesForOrganization("github").Returns(githubRepos.ToObservable()); apiClient.GetRepositoriesForOrganization("octokit").Returns(octokitRepos.ToObservable()); var cache = new InMemoryBlobCache(); var modelService = new ModelService(apiClient, cache, Substitute.For <IAvatarProvider>()); await modelService.InsertUser(new AccountCacheItem { Login = "******" }); var fetched = await modelService.GetRepositories().ToList(); Assert.Equal(4, fetched.Count); Assert.Equal(2, fetched[0].Count); Assert.Equal(4, fetched[1].Count); Assert.Equal(1, fetched[2].Count); Assert.Equal(3, fetched[3].Count); Assert.Equal("seegit", fetched[0][0].Name); Assert.Equal("codehaacks", fetched[0][1].Name); Assert.Equal("semver", fetched[1][0].Name); Assert.Equal("ninject", fetched[1][1].Name); Assert.Equal("jabbr", fetched[1][2].Name); Assert.Equal("nullguard", fetched[1][3].Name); Assert.Equal("visualstudio", fetched[2][0].Name); Assert.Equal("octokit.net", fetched[3][0].Name); Assert.Equal("octokit.rb", fetched[3][1].Name); Assert.Equal("octokit.objc", fetched[3][2].Name); var cachedOwnerRepositories = await cache.GetObject <IReadOnlyList <ModelService.RepositoryCacheItem> >("opus|Owner:repos"); Assert.Equal(2, cachedOwnerRepositories.Count); Assert.Equal("seegit", cachedOwnerRepositories[0].Name); Assert.Equal("haacked", cachedOwnerRepositories[0].Owner.Login); Assert.Equal("codehaacks", cachedOwnerRepositories[1].Name); Assert.Equal("haacked", cachedOwnerRepositories[1].Owner.Login); var cachedMemberRepositories = await cache.GetObject <IReadOnlyList <ModelService.RepositoryCacheItem> >("opus|Member:repos"); Assert.Equal(4, cachedMemberRepositories.Count); Assert.Equal("semver", cachedMemberRepositories[0].Name); Assert.Equal("mojombo", cachedMemberRepositories[0].Owner.Login); Assert.Equal("ninject", cachedMemberRepositories[1].Name); Assert.Equal("ninject", cachedMemberRepositories[1].Owner.Login); Assert.Equal("jabbr", cachedMemberRepositories[2].Name); Assert.Equal("jabbr", cachedMemberRepositories[2].Owner.Login); Assert.Equal("nullguard", cachedMemberRepositories[3].Name); Assert.Equal("fody", cachedMemberRepositories[3].Owner.Login); var cachedGitHubRepositories = await cache.GetObject <IReadOnlyList <ModelService.RepositoryCacheItem> >("opus|github|repos"); Assert.Equal(1, cachedGitHubRepositories.Count); Assert.Equal("seegit", cachedOwnerRepositories[0].Name); Assert.Equal("haacked", cachedOwnerRepositories[0].Owner.Login); Assert.Equal("codehaacks", cachedOwnerRepositories[1].Name); Assert.Equal("haacked", cachedOwnerRepositories[1].Owner.Login); var cachedOctokitRepositories = await cache.GetObject <IReadOnlyList <ModelService.RepositoryCacheItem> >("opus|octokit|repos"); Assert.Equal("octokit.net", cachedOctokitRepositories[0].Name); Assert.Equal("octokit", cachedOctokitRepositories[0].Owner.Login); Assert.Equal("octokit.rb", cachedOctokitRepositories[1].Name); Assert.Equal("octokit", cachedOctokitRepositories[1].Owner.Login); Assert.Equal("octokit.objc", cachedOctokitRepositories[2].Name); Assert.Equal("octokit", cachedOctokitRepositories[2].Owner.Login); }
public async Task ExpiredIndexReturnsLive() { var expected = 5; var username = "******"; var reponame = "repo"; var cache = new InMemoryBlobCache(); var apiClient = Substitute.For <IApiClient>(); var modelService = new ModelService(apiClient, cache, Substitute.For <IAvatarProvider>()); var user = CreateOctokitUser(username); apiClient.GetUser().Returns(Observable.Return(new UserAndScopes(user, null))); apiClient.GetOrganizations().Returns(Observable.Empty <Organization>()); var act = modelService.GetAccounts().ToEnumerable().First().First(); var repo = Substitute.For <ILocalRepositoryModel>(); repo.Name.Returns(reponame); repo.CloneUrl.Returns(new UriString("https://github.com/" + username + "/" + reponame)); var indexKey = string.Format(CultureInfo.InvariantCulture, "{0}|{1}:{2}", CacheIndex.PRPrefix, user.Login, repo.Name); var prcache = Enumerable.Range(1, expected) .Select(id => CreatePullRequest(user, id, ItemState.Open, "Cache " + id, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow)); // seed the cache prcache .Select(item => new ModelService.PullRequestCacheItem(item)) .Select(item => item.Save <ModelService.PullRequestCacheItem>(cache, indexKey).ToEnumerable().First()) .SelectMany(item => CacheIndex.AddAndSaveToIndex(cache, indexKey, item).ToEnumerable()) .ToList(); // expire the index var indexobj = await cache.GetObject <CacheIndex>(indexKey); indexobj.UpdatedAt = DateTimeOffset.UtcNow - TimeSpan.FromMinutes(6); await cache.InsertObject(indexKey, indexobj); var prlive = Observable.Range(1, expected) .Select(id => CreatePullRequest(user, id, ItemState.Open, "Live " + id, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow)) .DelaySubscription(TimeSpan.FromMilliseconds(10)); apiClient.GetPullRequestsForRepository(user.Login, repo.Name).Returns(prlive); await modelService.InsertUser(new AccountCacheItem(user)); ITrackingCollection <IPullRequestModel> col = new TrackingCollection <IPullRequestModel>(); modelService.GetPullRequests(repo, col); col.ProcessingDelay = TimeSpan.Zero; var count = 0; var done = new ReplaySubject <Unit>(); done.OnNext(Unit.Default); done.Subscribe(); col.Subscribe(t => { if (++count == expected * 2) { done.OnCompleted(); } }, () => { }); await done; Assert.Collection(col, col.Select(x => new Action <IPullRequestModel>(t => Assert.True(x.Title.StartsWith("Live")))).ToArray()); }
public async Task ExpiredIndexReturnsLive() { var expected = 5; var username = "******"; var reponame = "repo"; var cache = new InMemoryBlobCache(); var apiClient = Substitute.For<IApiClient>(); var modelService = new ModelService(apiClient, cache, Substitute.For<IAvatarProvider>()); var user = CreateOctokitUser(username); apiClient.GetUser().Returns(Observable.Return(new UserAndScopes(user, null))); apiClient.GetOrganizations().Returns(Observable.Empty<Organization>()); var act = modelService.GetAccounts().ToEnumerable().First().First(); var repo = Substitute.For<ILocalRepositoryModel>(); repo.Name.Returns(reponame); repo.CloneUrl.Returns(new UriString("https://github.com/" + username + "/" + reponame)); var indexKey = string.Format(CultureInfo.InvariantCulture, "{0}|{1}:{2}", CacheIndex.PRPrefix, user.Login, repo.Name); var prcache = Enumerable.Range(1, expected) .Select(id => CreatePullRequest(user, id, ItemState.Open, "Cache " + id, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow)); // seed the cache prcache .Select(item => new ModelService.PullRequestCacheItem(item)) .Select(item => item.Save<ModelService.PullRequestCacheItem>(cache, indexKey).ToEnumerable().First()) .SelectMany(item => CacheIndex.AddAndSaveToIndex(cache, indexKey, item).ToEnumerable()) .ToList(); // expire the index var indexobj = await cache.GetObject<CacheIndex>(indexKey); indexobj.UpdatedAt = DateTimeOffset.UtcNow - TimeSpan.FromMinutes(6); await cache.InsertObject(indexKey, indexobj); var prlive = Observable.Range(1, expected) .Select(id => CreatePullRequest(user, id, ItemState.Open, "Live " + id, DateTimeOffset.UtcNow, DateTimeOffset.UtcNow)) .DelaySubscription(TimeSpan.FromMilliseconds(10)); apiClient.GetPullRequestsForRepository(user.Login, repo.Name).Returns(prlive); await modelService.InsertUser(new AccountCacheItem(user)); ITrackingCollection<IPullRequestModel> col = new TrackingCollection<IPullRequestModel>(); modelService.GetPullRequests(repo, col); col.ProcessingDelay = TimeSpan.Zero; var count = 0; var done = new ReplaySubject<Unit>(); done.OnNext(Unit.Default); done.Subscribe(); col.Subscribe(t => { if (++count == expected * 2) { done.OnCompleted(); } }, () => { }); await done; Assert.Collection(col, col.Select(x => new Action<IPullRequestModel>(t => Assert.True(x.Title.StartsWith("Live")))).ToArray()); }