diff --git a/modules/docs/Volo.Docs.sln b/modules/docs/Volo.Docs.sln
index 7e78d7432f..0c3b43a821 100644
--- a/modules/docs/Volo.Docs.sln
+++ b/modules/docs/Volo.Docs.sln
@@ -1,7 +1,7 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.27703.1
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.28729.10
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{42416152-5BAB-4706-93A6-57A19E71FE14}"
EndProject
@@ -43,6 +43,18 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Docs.Admin.HttpApi.Cli
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Docs.Admin.Web", "src\Volo.Docs.Admin.Web\Volo.Docs.Admin.Web.csproj", "{116A6145-9D66-4867-B3EF-A464FAC47946}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{59D430A9-AC61-4457-8338-5DA0705ABB5D}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Docs.Application.Tests", "test\Volo.Docs.Application.Tests\Volo.Docs.Application.Tests.csproj", "{1BE0A197-55D0-40FF-A182-DBCF2E38D0C3}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Docs.Domain.Tests", "test\Volo.Docs.Domain.Tests\Volo.Docs.Domain.Tests.csproj", "{213F44A8-F9C1-4F04-9159-37E232FF18F2}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Docs.EntityFrameworkCore.Tests", "test\Volo.Docs.EntityFrameworkCore.Tests\Volo.Docs.EntityFrameworkCore.Tests.csproj", "{89F895EA-C4A0-4D91-9181-016F78459776}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Docs.TestBase", "test\Volo.Docs.TestBase\Volo.Docs.TestBase.csproj", "{C8BF652A-6DDF-4E5C-8CBA-BA5AFC50BFE2}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Volo.Docs.Admin.Application.Tests", "test\Volo.Docs.Admin.Application.Tests\Volo.Docs.Admin.Application.Tests.csproj", "{E9CF69BC-EEA6-4621-BE0E-64EE37C89807}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -109,6 +121,26 @@ Global
{116A6145-9D66-4867-B3EF-A464FAC47946}.Debug|Any CPU.Build.0 = Debug|Any CPU
{116A6145-9D66-4867-B3EF-A464FAC47946}.Release|Any CPU.ActiveCfg = Release|Any CPU
{116A6145-9D66-4867-B3EF-A464FAC47946}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1BE0A197-55D0-40FF-A182-DBCF2E38D0C3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1BE0A197-55D0-40FF-A182-DBCF2E38D0C3}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1BE0A197-55D0-40FF-A182-DBCF2E38D0C3}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1BE0A197-55D0-40FF-A182-DBCF2E38D0C3}.Release|Any CPU.Build.0 = Release|Any CPU
+ {213F44A8-F9C1-4F04-9159-37E232FF18F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {213F44A8-F9C1-4F04-9159-37E232FF18F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {213F44A8-F9C1-4F04-9159-37E232FF18F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {213F44A8-F9C1-4F04-9159-37E232FF18F2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {89F895EA-C4A0-4D91-9181-016F78459776}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {89F895EA-C4A0-4D91-9181-016F78459776}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {89F895EA-C4A0-4D91-9181-016F78459776}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {89F895EA-C4A0-4D91-9181-016F78459776}.Release|Any CPU.Build.0 = Release|Any CPU
+ {C8BF652A-6DDF-4E5C-8CBA-BA5AFC50BFE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C8BF652A-6DDF-4E5C-8CBA-BA5AFC50BFE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C8BF652A-6DDF-4E5C-8CBA-BA5AFC50BFE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C8BF652A-6DDF-4E5C-8CBA-BA5AFC50BFE2}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E9CF69BC-EEA6-4621-BE0E-64EE37C89807}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E9CF69BC-EEA6-4621-BE0E-64EE37C89807}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E9CF69BC-EEA6-4621-BE0E-64EE37C89807}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E9CF69BC-EEA6-4621-BE0E-64EE37C89807}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -132,6 +164,11 @@ Global
{262F38DB-62AF-427F-96E2-C6385C5AB695} = {BCA19441-17E9-43E6-AED1-15344D18F967}
{81EE378A-0DE1-47BA-86D9-08EF6317BB95} = {BCA19441-17E9-43E6-AED1-15344D18F967}
{116A6145-9D66-4867-B3EF-A464FAC47946} = {BCA19441-17E9-43E6-AED1-15344D18F967}
+ {1BE0A197-55D0-40FF-A182-DBCF2E38D0C3} = {59D430A9-AC61-4457-8338-5DA0705ABB5D}
+ {213F44A8-F9C1-4F04-9159-37E232FF18F2} = {59D430A9-AC61-4457-8338-5DA0705ABB5D}
+ {89F895EA-C4A0-4D91-9181-016F78459776} = {59D430A9-AC61-4457-8338-5DA0705ABB5D}
+ {C8BF652A-6DDF-4E5C-8CBA-BA5AFC50BFE2} = {59D430A9-AC61-4457-8338-5DA0705ABB5D}
+ {E9CF69BC-EEA6-4621-BE0E-64EE37C89807} = {59D430A9-AC61-4457-8338-5DA0705ABB5D}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {13691265-2547-4FFF-B757-E8FACB05679D}
diff --git a/modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj b/modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj
index f8143caa2f..9ad8e3bff6 100644
--- a/modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj
+++ b/modules/docs/src/Volo.Docs.Domain/Volo.Docs.Domain.csproj
@@ -18,6 +18,7 @@
+
diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainModule.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainModule.cs
index 80f42ed3ff..8adf03faa9 100644
--- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainModule.cs
+++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/DocsDomainModule.cs
@@ -1,4 +1,6 @@
-using Volo.Abp.Domain;
+using System;
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp.Domain;
using Volo.Abp.Localization;
using Volo.Abp.Modularity;
using Volo.Abp.VirtualFileSystem;
@@ -35,6 +37,11 @@ namespace Volo.Docs
options.Stores[GithubDocumentStore.Type] = typeof(GithubDocumentStore);
options.Stores[FileSystemDocumentStore.Type] = typeof(FileSystemDocumentStore);
});
+
+ context.Services.AddHttpClient(GithubRepositoryManager.HttpClientName, client =>
+ {
+ client.Timeout = TimeSpan.FromMilliseconds(15000);
+ });
}
}
}
diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentStore.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentStore.cs
index 5baa073b9c..715306ae5c 100644
--- a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentStore.cs
+++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubDocumentStore.cs
@@ -1,16 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Net;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
-using Octokit;
-using Octokit.Internal;
using Volo.Abp.Domain.Services;
using Volo.Docs.Documents;
using Volo.Docs.GitHub.Projects;
using Volo.Docs.Projects;
using Newtonsoft.Json.Linq;
+using Octokit;
using ProductHeaderValue = Octokit.ProductHeaderValue;
using Project = Volo.Docs.Projects.Project;
@@ -22,6 +20,13 @@ namespace Volo.Docs.GitHub.Documents
{
public const string Type = "GitHub";
+ private readonly IGithubRepositoryManager _githubRepositoryManager;
+
+ public GithubDocumentStore(IGithubRepositoryManager githubRepositoryManager)
+ {
+ _githubRepositoryManager = githubRepositoryManager;
+ }
+
public virtual async Task GetDocumentAsync(Project project, string documentName, string version)
{
var token = project.GetGitHubAccessTokenOrNull();
@@ -102,20 +107,7 @@ namespace Volo.Docs.GitHub.Documents
var url = project.GetGitHubUrl();
var ownerName = GetOwnerNameFromUrl(url);
var repositoryName = GetRepositoryNameFromUrl(url);
- var gitHubClient = CreateGitHubClient(project.GetGitHubAccessTokenOrNull());
-
- return await gitHubClient
- .Repository
- .Release
- .GetAll(ownerName, repositoryName);
- }
-
- private static GitHubClient CreateGitHubClient(string token = null)
- {
- //TODO: Why hard-coded "abpframework"? Should be configurable?
- return token.IsNullOrWhiteSpace()
- ? new GitHubClient(new ProductHeaderValue("abpframework"))
- : new GitHubClient(new ProductHeaderValue("abpframework"), new InMemoryCredentialStore(new Credentials(token)));
+ return await _githubRepositoryManager.GetReleasesAsync(ownerName, repositoryName, project.GetGitHubAccessTokenOrNull());
}
protected virtual string GetOwnerNameFromUrl(string url)
@@ -151,19 +143,7 @@ namespace Volo.Docs.GitHub.Documents
{
Logger.LogInformation("Downloading content from Github (DownloadWebContentAsStringAsync): " + rawUrl);
- using (var webClient = new GithubWebClient())
- {
- if (!token.IsNullOrWhiteSpace())
- {
- webClient.Headers.Add("Authorization", "token " + token);
- }
-
- webClient.Headers.Add("User-Agent", userAgent ?? "");
-
- //TODO: SET TIMEOUT?
-
- return await webClient.DownloadStringTaskAsync(new Uri(rawUrl));
- }
+ return await _githubRepositoryManager.GetFileRawStringContentAsync(rawUrl, token, userAgent);
}
catch (Exception ex)
{
@@ -179,16 +159,7 @@ namespace Volo.Docs.GitHub.Documents
{
Logger.LogInformation("Downloading content from Github (DownloadWebContentAsByteArrayAsync): " + rawUrl);
- using (var webClient = new GithubWebClient())
- {
- if (!token.IsNullOrWhiteSpace())
- {
- webClient.Headers.Add("Authorization", "token " + token);
- }
- webClient.Headers.Add("User-Agent", userAgent ?? "");
-
- return await webClient.DownloadDataTaskAsync(new Uri(rawUrl));
- }
+ return await _githubRepositoryManager.GetFileRawByteArrayContentAsync(rawUrl, token, userAgent);
}
catch (Exception ex)
{
@@ -237,21 +208,5 @@ namespace Volo.Docs.GitHub.Documents
.Replace("github.com", "raw.githubusercontent.com")
.ReplaceFirst("/tree/", "/");
}
-
- private class GithubWebClient : WebClient
- {
- protected override WebRequest GetWebRequest(Uri address)
- {
- var webRequest = base.GetWebRequest(address);
- if (webRequest == null)
- {
- return null;
- }
-
- webRequest.Timeout = 15000;
-
- return webRequest;
- }
- }
}
}
\ No newline at end of file
diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubRepositoryManager.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubRepositoryManager.cs
new file mode 100644
index 0000000000..be71fd4772
--- /dev/null
+++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/GithubRepositoryManager.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Threading.Tasks;
+using Octokit;
+using Octokit.Internal;
+using ProductHeaderValue = Octokit.ProductHeaderValue;
+
+namespace Volo.Docs.GitHub.Documents
+{
+ public class GithubRepositoryManager : IGithubRepositoryManager
+ {
+ public const string HttpClientName = "GithubRepositoryManagerHttpClientName";
+
+ private readonly IHttpClientFactory _clientFactory;
+
+ public GithubRepositoryManager(IHttpClientFactory clientFactory)
+ {
+ _clientFactory = clientFactory;
+ }
+
+ public async Task GetFileRawStringContentAsync(string rawUrl, string token, string userAgent)
+ {
+ var httpClient = _clientFactory.CreateClient(HttpClientName);
+ if (!token.IsNullOrWhiteSpace())
+ {
+ httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", token);
+ }
+
+ httpClient.DefaultRequestHeaders.Add("User-Agent", userAgent ?? "");
+
+ return await httpClient.GetStringAsync(new Uri(rawUrl));
+ }
+
+ public async Task GetFileRawByteArrayContentAsync(string rawUrl, string token, string userAgent)
+ {
+ var httpClient = _clientFactory.CreateClient(HttpClientName);
+ if (!token.IsNullOrWhiteSpace())
+ {
+ httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", token);
+ }
+
+ httpClient.DefaultRequestHeaders.Add("User-Agent", userAgent ?? "");
+
+ return await httpClient.GetByteArrayAsync(new Uri(rawUrl));
+ }
+
+ public async Task> GetReleasesAsync(string name, string repositoryName, string token)
+ {
+ var client = token.IsNullOrWhiteSpace()
+ ? new GitHubClient(new ProductHeaderValue(name))
+ : new GitHubClient(new ProductHeaderValue(name), new InMemoryCredentialStore(new Credentials(token)));
+
+ return (await client
+ .Repository
+ .Release
+ .GetAll(name, repositoryName)).ToList();
+ }
+ }
+}
diff --git a/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/IGithubRepositoryManager.cs b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/IGithubRepositoryManager.cs
new file mode 100644
index 0000000000..519b9829d5
--- /dev/null
+++ b/modules/docs/src/Volo.Docs.Domain/Volo/Docs/GitHub/Documents/IGithubRepositoryManager.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Octokit;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Docs.GitHub.Documents
+{
+ public interface IGithubRepositoryManager : ITransientDependency
+ {
+ Task GetFileRawStringContentAsync(string rawUrl, string token, string userAgent);
+
+ Task GetFileRawByteArrayContentAsync(string rawUrl, string token, string userAgent);
+
+ Task> GetReleasesAsync(string name, string repositoryName, string token);
+
+ }
+}
diff --git a/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Projects/EfCoreProjectRepository.cs b/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Projects/EfCoreProjectRepository.cs
index f9e6ab4fa7..0df6f12c42 100644
--- a/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Projects/EfCoreProjectRepository.cs
+++ b/modules/docs/src/Volo.Docs.EntityFrameworkCore/Volo/Docs/Projects/EfCoreProjectRepository.cs
@@ -21,7 +21,7 @@ namespace Volo.Docs.Projects
public async Task> GetListAsync(string sorting, int maxResultCount, int skipCount)
{
- var projects = await DbSet.OrderBy(sorting ?? "creationTime desc")
+ var projects = await DbSet.OrderBy(sorting ?? "Id desc")
.PageBy(skipCount, maxResultCount)
.ToListAsync();
diff --git a/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo.Docs.Admin.Application.Tests.csproj b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo.Docs.Admin.Application.Tests.csproj
new file mode 100644
index 0000000000..56ff4979d2
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo.Docs.Admin.Application.Tests.csproj
@@ -0,0 +1,18 @@
+
+
+
+ netcoreapp2.2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/ApplicationService_Tests.cs b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/ApplicationService_Tests.cs
new file mode 100644
index 0000000000..638913d971
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/ApplicationService_Tests.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Shouldly;
+using Volo.Docs.Admin.Projects;
+using Volo.Docs.Projects;
+using Xunit;
+
+namespace Volo.Docs
+{
+ public class ApplicationService_Tests : DocsApplicationTestBase
+ {
+ private readonly IProjectAppService _projectAppService;
+ private readonly IProjectRepository _projectRepository;
+ private readonly DocsTestData _testData;
+
+ public ApplicationService_Tests()
+ {
+ _projectRepository = GetRequiredService();
+ _projectAppService = GetRequiredService();
+ _testData = GetRequiredService();
+ }
+
+ [Fact]
+ public async Task GetListAsync()
+ {
+ var projects = await _projectAppService.GetListAsync();
+ projects.ShouldNotBeNull();
+ projects.Items.Count.ShouldBe(1);
+ projects.Items.ShouldContain(x => x.Id == _testData.PorjectId);
+ }
+
+ [Fact]
+ public async Task GetAsync()
+ {
+ var project = await _projectAppService.GetAsync("ABP");
+ project.ShouldNotBeNull();
+ project.ShortName.ShouldBe("ABP");
+ }
+
+ [Fact]
+ public async Task GetVersionsAsync()
+ {
+ // TODO: Need to mock WebClient and Octokit components
+ }
+
+ }
+
+}
diff --git a/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/DocsApplicationTestBase.cs b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/DocsApplicationTestBase.cs
new file mode 100644
index 0000000000..25ce81871d
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/DocsApplicationTestBase.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Volo.Docs
+{
+ public class DocsAdminApplicationTestBase : DocsTestBase
+ {
+
+ }
+
+ public class DocsApplicationTestBase : DocsTestBase
+ {
+
+ }
+}
diff --git a/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/DocsApplicationTestModule.cs b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/DocsApplicationTestModule.cs
new file mode 100644
index 0000000000..8212a816a6
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/DocsApplicationTestModule.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Volo.Abp.Modularity;
+using Volo.Docs.Admin;
+
+namespace Volo.Docs
+{
+ [DependsOn(
+ typeof(DocsApplicationModule),
+ typeof(DocsDomainTestModule)
+ )]
+ public class DocsApplicationTestModule : AbpModule
+ {
+
+ }
+
+ [DependsOn(
+ typeof(DocsAdminApplicationModule),
+ typeof(DocsDomainTestModule)
+ )]
+ public class DocsAdminApplicationTestModule : AbpModule
+ {
+
+ }
+}
diff --git a/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/ProjectAdminAppService_Tests.cs b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/ProjectAdminAppService_Tests.cs
new file mode 100644
index 0000000000..dac273a9ba
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.Admin.Application.Tests/Volo/Docs/ProjectAdminAppService_Tests.cs
@@ -0,0 +1,124 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Shouldly;
+using Volo.Abp.Application.Dtos;
+using Volo.Docs.Admin.Projects;
+using Volo.Docs.Projects;
+using Xunit;
+
+namespace Volo.Docs
+{
+ public class ProjectAdminAppService_Tests : DocsAdminApplicationTestBase
+ {
+ private readonly IProjectAdminAppService _projectAdminAppService;
+ private readonly IProjectRepository _projectRepository;
+ private readonly DocsTestData _testData;
+
+ public ProjectAdminAppService_Tests()
+ {
+ _projectRepository = GetRequiredService();
+ _projectAdminAppService = GetRequiredService();
+ _testData = GetRequiredService();
+ }
+
+ [Fact]
+ public async Task GetListAsync()
+ {
+ var projects = await _projectAdminAppService.GetListAsync(new PagedAndSortedResultRequestDto());
+ projects.ShouldNotBeNull();
+ projects.TotalCount.ShouldBe(1);
+ projects.Items.ShouldContain(x => x.Name == "ABP vNext");
+ }
+
+ [Fact]
+ public async Task GetAsync()
+ {
+ var project = await _projectAdminAppService.GetAsync(_testData.PorjectId);
+ project.ShouldNotBeNull();
+ project.Id.ShouldBe(_testData.PorjectId);
+ }
+
+ [Fact]
+ public async Task CreateAsync()
+ {
+ var createProjectDto = new CreateProjectDto
+ {
+ Name = "ABP vNext",
+ ShortName = "ABP",
+ Format = "md",
+ DefaultDocumentName = "index",
+ NavigationDocumentName = "docs-nav.json",
+ MinimumVersion = "1",
+ MainWebsiteUrl = "abp.io",
+ LatestVersionBranchName = "",
+ DocumentStoreType = "GitHub",
+ ExtraProperties = new Dictionary()
+ };
+ createProjectDto.ExtraProperties.Add("GitHubRootUrl",
+ "https://github.com/abpframework/abp/tree/{version}/docs/en/");
+ createProjectDto.ExtraProperties.Add("GitHubAccessToken", "123456");
+ createProjectDto.ExtraProperties.Add("GitHubUserAgent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
+
+ //Act
+ var projectDto = await _projectAdminAppService.CreateAsync(createProjectDto);
+ projectDto.ShouldNotBeNull();
+ projectDto.Name.ShouldBe(createProjectDto.Name);
+ projectDto.ShortName.ShouldBe(createProjectDto.ShortName);
+ projectDto.Format.ShouldBe(createProjectDto.Format);
+ projectDto.DefaultDocumentName.ShouldBe(createProjectDto.DefaultDocumentName);
+ projectDto.NavigationDocumentName.ShouldBe(createProjectDto.NavigationDocumentName);
+ projectDto.MinimumVersion.ShouldBe(createProjectDto.MinimumVersion);
+ projectDto.MainWebsiteUrl.ShouldBe(createProjectDto.MainWebsiteUrl);
+ projectDto.LatestVersionBranchName.ShouldBe(createProjectDto.LatestVersionBranchName);
+ projectDto.DocumentStoreType.ShouldBe(createProjectDto.DocumentStoreType);
+
+ projectDto.ExtraProperties.Except(createProjectDto.ExtraProperties).Any().ShouldBe(false);
+
+ }
+
+ [Fact]
+ public async Task UpdateAsync()
+ {
+ var updateProjectDto = new UpdateProjectDto
+ {
+ Name = "ABP vNext",
+ Format = "md",
+ DefaultDocumentName = "index",
+ NavigationDocumentName = "docs-nav.json",
+
+ MinimumVersion = "1",
+ MainWebsiteUrl = "abp.io",
+ LatestVersionBranchName = "",
+ ExtraProperties = new Dictionary()
+ };
+ updateProjectDto.ExtraProperties.Add("test", "test");
+
+ var projectDto = await _projectAdminAppService.UpdateAsync(_testData.PorjectId, updateProjectDto);
+
+
+ projectDto.ShouldNotBeNull();
+ projectDto.Name.ShouldBe(updateProjectDto.Name);
+
+ projectDto.Format.ShouldBe(updateProjectDto.Format);
+ projectDto.DefaultDocumentName.ShouldBe(updateProjectDto.DefaultDocumentName);
+ projectDto.NavigationDocumentName.ShouldBe(updateProjectDto.NavigationDocumentName);
+ projectDto.MinimumVersion.ShouldBe(updateProjectDto.MinimumVersion);
+ projectDto.MainWebsiteUrl.ShouldBe(updateProjectDto.MainWebsiteUrl);
+ projectDto.LatestVersionBranchName.ShouldBe(updateProjectDto.LatestVersionBranchName);
+ projectDto.ExtraProperties.ShouldContainKey("test");
+ }
+
+ [Fact]
+ public async Task DeleteAsync()
+ {
+ (await _projectRepository.GetAsync(_testData.PorjectId)).ShouldNotBeNull();
+
+ await _projectAdminAppService.DeleteAsync(_testData.PorjectId);
+
+ (await _projectRepository.GetListAsync()).ShouldBeEmpty();
+ }
+ }
+}
diff --git a/modules/docs/test/Volo.Docs.Application.Tests/Volo.Docs.Application.Tests.csproj b/modules/docs/test/Volo.Docs.Application.Tests/Volo.Docs.Application.Tests.csproj
new file mode 100644
index 0000000000..87144ccfe9
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.Application.Tests/Volo.Docs.Application.Tests.csproj
@@ -0,0 +1,17 @@
+
+
+
+ netcoreapp2.2
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/docs/test/Volo.Docs.Application.Tests/Volo/Docs/DocsApplicationTestModule.cs b/modules/docs/test/Volo.Docs.Application.Tests/Volo/Docs/DocsApplicationTestModule.cs
new file mode 100644
index 0000000000..62d027c220
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.Application.Tests/Volo/Docs/DocsApplicationTestModule.cs
@@ -0,0 +1,13 @@
+using Volo.Abp.Modularity;
+
+namespace Volo.Docs
+{
+ [DependsOn(
+ typeof(DocsApplicationModule),
+ typeof(DocsDomainTestModule)
+ )]
+ public class DocsApplicationTestModule : AbpModule
+ {
+
+ }
+}
diff --git a/modules/docs/test/Volo.Docs.Domain.Tests/Volo.Docs.Domain.Tests.csproj b/modules/docs/test/Volo.Docs.Domain.Tests/Volo.Docs.Domain.Tests.csproj
new file mode 100644
index 0000000000..ba1185b397
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.Domain.Tests/Volo.Docs.Domain.Tests.csproj
@@ -0,0 +1,17 @@
+
+
+
+ netcoreapp2.2
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocsDomainTestBase.cs b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocsDomainTestBase.cs
new file mode 100644
index 0000000000..7de6bb3182
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocsDomainTestBase.cs
@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using Microsoft.Extensions.DependencyInjection;
+using NSubstitute;
+using Octokit;
+using Volo.Docs.GitHub.Documents;
+
+namespace Volo.Docs
+{
+ public abstract class DocsDomainTestBase : DocsTestBase
+ {
+ protected override void AfterAddApplication(IServiceCollection services)
+ {
+ var repositoryManager = Substitute.For();
+ repositoryManager.GetFileRawStringContentAsync(Arg.Any(), Arg.Any(), Arg.Any())
+ .Returns("stringContent");
+ repositoryManager.GetFileRawByteArrayContentAsync(Arg.Any(), Arg.Any(), Arg.Any())
+ .Returns(new byte[] { 0x01, 0x02, 0x03 });
+ repositoryManager.GetReleasesAsync(Arg.Any(), Arg.Any(), Arg.Any())
+ .Returns(new List
+ {
+ new Release("https://api.github.com/repos/abpframework/abp/releases/16293679",
+ "https://github.com/abpframework/abp/releases/tag/0.15.0",
+ "https://api.github.com/repos/abpframework/abp/releases/16293679/assets",
+ "https://uploads.github.com/repos/abpframework/abp/releases/16293679/assets{?name,label}",
+ 16293679,
+ "0.15.0",
+ "master",
+ "0.15.0",
+ "0.15.0 already release",
+ false,
+ false,
+ DateTimeOffset.Parse("2019-03-22T18:43:58Z"),
+ DateTimeOffset.Parse("2019-03-22T19:44:25Z"),
+ null,
+ "https://api.github.com/repos/abpframework/abp/tarball/0.15.0",
+ "https://api.github.com/repos/abpframework/abp/zipball/0.15.0",
+ null)
+ });
+ services.AddSingleton(repositoryManager);
+ }
+ }
+}
diff --git a/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocsDomainTestModule.cs b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocsDomainTestModule.cs
new file mode 100644
index 0000000000..c8ed977193
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocsDomainTestModule.cs
@@ -0,0 +1,14 @@
+using Volo.Docs.EntityFrameworkCore;
+using Volo.Abp.Modularity;
+
+namespace Volo.Docs
+{
+ [DependsOn(
+ typeof(DocsEntityFrameworkCoreTestModule),
+ typeof(DocsTestBaseModule)
+ )]
+ public class DocsDomainTestModule : AbpModule
+ {
+
+ }
+}
diff --git a/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocumentStoreFactory_Tests.cs b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocumentStoreFactory_Tests.cs
new file mode 100644
index 0000000000..afd28bb8ca
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/DocumentStoreFactory_Tests.cs
@@ -0,0 +1,28 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Shouldly;
+using Volo.Docs.Documents;
+using Volo.Docs.FileSystem.Documents;
+using Volo.Docs.GitHub.Documents;
+using Xunit;
+
+namespace Volo.Docs
+{
+ public class DocumentStoreFactory_Tests : DocsDomainTestBase
+ {
+ private readonly IDocumentStoreFactory _documentStoreFactory;
+
+ public DocumentStoreFactory_Tests()
+ {
+ _documentStoreFactory = GetRequiredService();
+ }
+
+ [Fact]
+ public void Create()
+ {
+ _documentStoreFactory.Create(GithubDocumentStore.Type).GetType().ShouldBe(typeof(GithubDocumentStore));
+ _documentStoreFactory.Create(FileSystemDocumentStore.Type).GetType().ShouldBe(typeof(FileSystemDocumentStore));
+ }
+ }
+}
diff --git a/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/GithubDocumentStore_Tests.cs b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/GithubDocumentStore_Tests.cs
new file mode 100644
index 0000000000..ac04ec0e46
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/GithubDocumentStore_Tests.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using NSubstitute;
+using Octokit;
+using Shouldly;
+using Volo.Docs.Documents;
+using Volo.Docs.GitHub.Documents;
+using Volo.Docs.Projects;
+using Xunit;
+
+namespace Volo.Docs
+{
+ public class GithubDocumentStore_Tests : DocsDomainTestBase
+ {
+ private readonly IDocumentStoreFactory _documentStoreFactory;
+ private readonly IProjectRepository _projectRepository;
+ private readonly DocsTestData _testData;
+
+ public GithubDocumentStore_Tests()
+ {
+ _documentStoreFactory = GetRequiredService();
+ _projectRepository = GetRequiredService();
+ _testData = GetRequiredService();
+ }
+
+ [Fact]
+ public async Task GetDocumentAsync()
+ {
+ var store = _documentStoreFactory.Create(GithubDocumentStore.Type);
+ var project = await _projectRepository.FindAsync(_testData.PorjectId);
+ project.ShouldNotBeNull();
+ var document = await store.GetDocumentAsync(project, "index2", "0.123.0");
+ document.ShouldNotBeNull();
+
+ document.Title.ShouldBe("index2");
+ document.FileName.ShouldBe("index2");
+ document.Version.ShouldBe("0.123.0");
+ document.Content.ShouldBe("stringContent");
+ }
+
+ [Fact]
+ public async Task GetVersionsAsync()
+ {
+ var store = _documentStoreFactory.Create(GithubDocumentStore.Type);
+ var project = await _projectRepository.FindAsync(_testData.PorjectId);
+ project.ShouldNotBeNull();
+
+ var document = await store.GetVersionsAsync(project);
+ document.ShouldNotBeNull();
+
+ document.Count.ShouldBe(1);
+ document.ShouldContain(x => x.Name == "0.15.0" && x.DisplayName == "0.15.0");
+ }
+
+ [Fact]
+ public async Task GetResource()
+ {
+ var store = _documentStoreFactory.Create(GithubDocumentStore.Type);
+ var project = await _projectRepository.FindAsync(_testData.PorjectId);
+ project.ShouldNotBeNull();
+
+ var documentResource = await store.GetResource(project, "index.md", "0.123.0");
+ documentResource.ShouldNotBeNull();
+
+ documentResource.Content.ShouldBe(new byte[]
+ {
+ 0x01, 0x02, 0x03
+ });
+ }
+ }
+}
diff --git a/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/Project_Tests.cs b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/Project_Tests.cs
new file mode 100644
index 0000000000..808ff88afa
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.Domain.Tests/Volo/Docs/Project_Tests.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using Shouldly;
+using Volo.Docs.Projects;
+using Xunit;
+
+namespace Volo.Docs
+{
+ public class Project_Tests : DocsDomainTestBase
+ {
+ [Theory]
+ [InlineData("aaa")]
+ [InlineData("bbb")]
+ public void SetName(string name)
+ {
+ var project = new Project(Guid.NewGuid(), "ABP vNext", "ABP", "Github", "md", "index",
+ "docs-nav.json");
+ project.SetName(name);
+ project.Name.ShouldBe(name);
+ }
+
+ [Theory]
+ [InlineData("aaa")]
+ [InlineData("bbb")]
+ public void SetFormat(string format)
+ {
+ var project = new Project(Guid.NewGuid(), "ABP vNext", "ABP", "Github", "md", "index",
+ "docs-nav.json");
+ project.SetFormat(format);
+ project.Format.ShouldBe(format);
+ }
+
+ [Theory]
+ [InlineData("aaa")]
+ [InlineData("bbb")]
+ public void SetNavigationDocumentName(string navigationDocumentName)
+ {
+ var project = new Project(Guid.NewGuid(), "ABP vNext", "ABP", "Github", "md", "index",
+ "docs-nav.json");
+ project.SetNavigationDocumentName(navigationDocumentName);
+ project.NavigationDocumentName.ShouldBe(navigationDocumentName);
+ }
+
+ [Theory]
+ [InlineData("aaa")]
+ [InlineData("bbb")]
+ public void SetDefaultDocumentName(string defaultDocumentName)
+ {
+ var project = new Project(Guid.NewGuid(), "ABP vNext", "ABP", "Github", "md", "index",
+ "docs-nav.json");
+ project.SetDefaultDocumentName(defaultDocumentName);
+ project.DefaultDocumentName.ShouldBe(defaultDocumentName);
+ }
+ }
+}
diff --git a/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo.Docs.EntityFrameworkCore.Tests.csproj b/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo.Docs.EntityFrameworkCore.Tests.csproj
new file mode 100644
index 0000000000..666c1e813f
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo.Docs.EntityFrameworkCore.Tests.csproj
@@ -0,0 +1,20 @@
+
+
+
+ netcoreapp2.2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo/Docs/EntityFrameworkCore/DocsEntityFrameworkCoreTestModule.cs b/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo/Docs/EntityFrameworkCore/DocsEntityFrameworkCoreTestModule.cs
new file mode 100644
index 0000000000..8a73b060cc
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo/Docs/EntityFrameworkCore/DocsEntityFrameworkCoreTestModule.cs
@@ -0,0 +1,42 @@
+using Microsoft.Data.Sqlite;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Storage;
+using Volo.Abp.EntityFrameworkCore;
+using Volo.Abp.Modularity;
+using Volo.Docs.EntityFrameworkCore;
+
+namespace Volo.Docs
+{
+ [DependsOn(
+ typeof(DocsTestBaseModule),
+ typeof(DocsEntityFrameworkCoreModule)
+ )]
+ public class DocsEntityFrameworkCoreTestModule : AbpModule
+ {
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ var sqliteConnection = CreateDatabaseAndGetConnection();
+
+ Configure(options =>
+ {
+ options.Configure(abpDbContextConfigurationContext =>
+ {
+ abpDbContextConfigurationContext.DbContextOptions.UseSqlite(sqliteConnection);
+ });
+ });
+ }
+
+ private static SqliteConnection CreateDatabaseAndGetConnection()
+ {
+ var connection = new SqliteConnection("Data Source=:memory:");
+ connection.Open();
+
+ new DocsDbContext(
+ new DbContextOptionsBuilder().UseSqlite(connection).Options
+ ).GetService().CreateTables();
+
+ return connection;
+ }
+ }
+}
diff --git a/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo/Docs/EntityFrameworkCore/ProjectRepository_Tests.cs b/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo/Docs/EntityFrameworkCore/ProjectRepository_Tests.cs
new file mode 100644
index 0000000000..8aa3919905
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.EntityFrameworkCore.Tests/Volo/Docs/EntityFrameworkCore/ProjectRepository_Tests.cs
@@ -0,0 +1,11 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Volo.Docs.EntityFrameworkCore
+{
+ public class ProjectRepository_Tests : ProjectRepository_Tests
+ {
+
+ }
+}
diff --git a/modules/docs/test/Volo.Docs.TestBase/Volo.Docs.TestBase.csproj b/modules/docs/test/Volo.Docs.TestBase/Volo.Docs.TestBase.csproj
new file mode 100644
index 0000000000..a92946140a
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.TestBase/Volo.Docs.TestBase.csproj
@@ -0,0 +1,26 @@
+
+
+
+ netcoreapp2.2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestBase.cs b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestBase.cs
new file mode 100644
index 0000000000..2ceb4b8f95
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestBase.cs
@@ -0,0 +1,14 @@
+using Volo.Abp;
+using Volo.Abp.Modularity;
+
+namespace Volo.Docs
+{
+ public abstract class DocsTestBase : AbpIntegratedTest
+ where TStartupModule : IAbpModule
+ {
+ protected override void SetAbpApplicationCreationOptions(AbpApplicationCreationOptions options)
+ {
+ options.UseAutofac();
+ }
+ }
+}
diff --git a/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestBaseModule.cs b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestBaseModule.cs
new file mode 100644
index 0000000000..a4661075d0
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestBaseModule.cs
@@ -0,0 +1,37 @@
+using Microsoft.Extensions.DependencyInjection;
+using Volo.Abp;
+using Volo.Abp.Authorization;
+using Volo.Abp.Autofac;
+using Volo.Abp.Modularity;
+
+namespace Volo.Docs
+{
+ [DependsOn(
+ typeof(AbpAutofacModule),
+ typeof(AbpTestBaseModule),
+ typeof(AbpAuthorizationModule),
+ typeof(DocsDomainModule)
+ )]
+ public class DocsTestBaseModule : AbpModule
+ {
+ public override void ConfigureServices(ServiceConfigurationContext context)
+ {
+ context.Services.AddAlwaysAllowAuthorization();
+ }
+
+ public override void OnApplicationInitialization(ApplicationInitializationContext context)
+ {
+ SeedTestData(context);
+ }
+
+ private static void SeedTestData(ApplicationInitializationContext context)
+ {
+ using (var scope = context.ServiceProvider.CreateScope())
+ {
+ scope.ServiceProvider
+ .GetRequiredService()
+ .Build();
+ }
+ }
+ }
+}
diff --git a/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestData.cs b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestData.cs
new file mode 100644
index 0000000000..829195efe6
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestData.cs
@@ -0,0 +1,10 @@
+using System;
+using Volo.Abp.DependencyInjection;
+
+namespace Volo.Docs
+{
+ public class DocsTestData : ISingletonDependency
+ {
+ public Guid PorjectId { get; } = Guid.NewGuid();
+ }
+}
diff --git a/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestDataBuilder.cs b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestDataBuilder.cs
new file mode 100644
index 0000000000..7950ea8348
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/DocsTestDataBuilder.cs
@@ -0,0 +1,34 @@
+using Volo.Abp.DependencyInjection;
+using Volo.Abp.Guids;
+using Volo.Docs.GitHub.Documents;
+using Volo.Docs.Projects;
+
+namespace Volo.Docs
+{
+ public class DocsTestDataBuilder : ITransientDependency
+ {
+ private readonly IGuidGenerator _guidGenerator;
+ private DocsTestData _testData;
+ private IProjectRepository _projectRepository;
+
+ public DocsTestDataBuilder(
+ IGuidGenerator guidGenerator,
+ DocsTestData testData, IProjectRepository projectRepository)
+ {
+ _guidGenerator = guidGenerator;
+ _testData = testData;
+ _projectRepository = projectRepository;
+ }
+
+ public void Build()
+ {
+ var project = new Project(_testData.PorjectId, "ABP vNext", "ABP", GithubDocumentStore.Type, "md", "index",
+ "docs-nav.json");
+ project.ExtraProperties.Add("GitHubRootUrl", "https://github.com/abpframework/abp/tree/{version}/docs/en/");
+ project.ExtraProperties.Add("GitHubAccessToken", "123456");
+ project.ExtraProperties.Add("GitHubUserAgent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
+
+ _projectRepository.Insert(project);
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/ProjectRepository_Tests.cs b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/ProjectRepository_Tests.cs
new file mode 100644
index 0000000000..66c306bd4e
--- /dev/null
+++ b/modules/docs/test/Volo.Docs.TestBase/Volo/Docs/ProjectRepository_Tests.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
+using Shouldly;
+using Volo.Abp.Modularity;
+using Volo.Docs.Projects;
+using Xunit;
+
+namespace Volo.Docs
+{
+ public abstract class ProjectRepository_Tests : DocsTestBase
+ where TStartupModule : IAbpModule
+ {
+
+ protected readonly IProjectRepository _projectRepository;
+
+ protected ProjectRepository_Tests()
+ {
+ _projectRepository = GetRequiredService(); ;
+ }
+
+ [Fact]
+ public async Task GetListAsync()
+ {
+ var projects = await _projectRepository.GetListAsync();
+
+ projects.ShouldNotBeNull();
+ projects.Count.ShouldBe(1);
+ }
+
+ [Fact]
+ public async Task GetTotalProjectCount()
+ {
+ var count = await _projectRepository.GetTotalProjectCount();
+
+ count.ShouldBe(1);
+ }
+
+ [Fact]
+ public async Task GetByShortNameAsync()
+ {
+ var project = await _projectRepository.GetByShortNameAsync("ABP");
+
+ project.ShouldNotBeNull();
+ project.ShortName.ShouldBe("ABP");
+ }
+ }
+}