Skip to content

Commit

Permalink
split dev server out from main Nessie project
Browse files Browse the repository at this point in the history
  • Loading branch information
waf committed Nov 25, 2018
1 parent 3450f6a commit 5565a42
Show file tree
Hide file tree
Showing 18 changed files with 807 additions and 8 deletions.
89 changes: 89 additions & 0 deletions DevServer.Tests/AutoRefreshTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using System.Collections.Generic;
using System.IO;
using System.IO.Abstractions.TestingHelpers;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace DevServer.Tests
{
public class AutoRefreshTests
{
private readonly AutoRefresh autorefresh;

public AutoRefreshTests()
{
this.autorefresh = new AutoRefresh();
}

[Fact]
public async Task KeepAliveLoop_CancelRequested_IsCanceled()
{
var tokenSource = new CancellationTokenSource();

// system under test
var task = autorefresh.KeepAliveLoop(tokenSource.Token);
tokenSource.Cancel();

await Assert.ThrowsAsync<TaskCanceledException>(() => task);
}

[Fact]
public void SendPollResponse_WithTwoRefreshes_ContainsTwoRefreshes()
{
autorefresh.SendClientRefresh();
autorefresh.SendClientRefresh();

var response = autorefresh.SendPollResponse();

Assert.Equal(
"data: refresh\r\n\r\ndata: refresh\r\n\r\n",
new StreamReader(response.Body).ReadToEnd()
);
}

[Fact]
public async Task AppendAutoRefreshJavaScript_HtmlFile_AppendsAutoRefreshJavaScript()
{
var response = CreateMemoryStreamResponse("howdy", "text/html");

// system under test
await autorefresh.AppendAutoRefreshJavaScript(response, response.Body, CancellationToken.None);

response.Body.Position = 0;
var content = new StreamReader(response.Body).ReadToEnd();

Assert.Contains("howdy", content);
Assert.Contains("window.location.reload", content);

response.Body.Dispose();
}

[Fact]
public async Task AppendAutoRefreshJavaScript_NotHtmlFile_DoesNotAppendAutoRefreshJavaScript()
{
var response = CreateMemoryStreamResponse("{}", "application/json");

// system under test
await autorefresh.AppendAutoRefreshJavaScript(response, response.Body, CancellationToken.None);

response.Body.Position = 0;
var content = new StreamReader(response.Body).ReadToEnd();

Assert.Equal("{}", content.Trim());

response.Body.Dispose();
}

private static HttpServerResponse CreateMemoryStreamResponse(string content, string contentType)
{
var stream = new MemoryStream();
var writer = new StreamWriter(stream);
writer.WriteLine(content);
writer.Flush();
var response = new HttpServerResponse(HttpStatusCode.OK, new WebHeaderCollection(), stream, contentType);
return response;
}
}
}
43 changes: 43 additions & 0 deletions DevServer.Tests/DevServer.Tests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<None Remove="IntegrationTests\WebRoot\fruits\apple.html" />
<None Remove="IntegrationTests\WebRoot\fruits\pineapple.html" />
<None Remove="IntegrationTests\WebRoot\fruits\tomato.html" />
<None Remove="IntegrationTests\WebRoot\index.html" />
</ItemGroup>

<ItemGroup>
<Content Include="IntegrationTests\WebRoot\fruits\apple.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="IntegrationTests\WebRoot\fruits\pineapple.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="IntegrationTests\WebRoot\fruits\tomato.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="IntegrationTests\WebRoot\index.html">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
<PackageReference Include="System.IO.Abstractions" Version="2.1.0.256" />
<PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="2.1.0.256" />
<PackageReference Include="xunit" Version="2.3.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.3.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\DevServer\DevServer.csproj" />
</ItemGroup>

</Project>
57 changes: 57 additions & 0 deletions DevServer.Tests/HttpServerResponseTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Abstractions.TestingHelpers;
using System.Net;
using System.Text;
using Xunit;

namespace DevServer.Tests
{
public class HttpServerResponseTests
{
[Fact]
public void HtmlResponse_DefaultParams_IsSuccessfulHtmlResponse()
{
var response = HttpServerResponse.HtmlResponse("Hello");

Assert.Equal("Hello", new StreamReader(response.Body).ReadToEnd());
Assert.Equal("text/html", response.ContentType);
Assert.Equal(200, (int)response.StatusCode);

response.Body.Dispose();
}

[Fact]
public void StringResponse_WithSpecifiedParameters_UsesArguments()
{
var response = HttpServerResponse.StringResponse("{}", "application/json", HttpStatusCode.OK);

Assert.Equal("{}", new StreamReader(response.Body).ReadToEnd());
Assert.Equal("application/json", response.ContentType);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);

response.Body.Dispose();
}

[Fact]
public void FileResponse_WithModifiedFileTime_SetsAsLastModifiedTimeHeader()
{
var fs = new MockFileSystem(new Dictionary<string, MockFileData>
{
[@"C:\myserver\hello.json"] = new MockFileData("{}")
});
// set file modification time
var written = DateTime.Now.AddDays(-1);
fs.File.SetLastWriteTime(@"C:\myserver\hello.json", written);

// system under test
var response = HttpServerResponse.FileResponse(fs, @"C:\myserver\hello.json");

Assert.Equal("{}", new StreamReader(response.Body).ReadToEnd());
Assert.Equal(written.ToString("r"), response.Headers[HttpRequestHeader.LastModified]);

response.Body.Dispose();
}
}
}
60 changes: 60 additions & 0 deletions DevServer.Tests/IntegrationTests/IntegrationTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace DevServer.Tests
{
/// <summary>
/// Integration tests. These tests create and assert against a running webserver!
/// </summary>
public class IntegrationTests
{
[Fact]
[Trait("Category", "Integration")]
public async Task ServeWebsite()
{
var http = new HttpClient();
var cwd = Path.Combine(
Directory.GetCurrentDirectory(),
@"IntegrationTests\WebRoot\"
);
using(var server = new HttpServer(cwd, "localhost", 8080, enableAutorefresh: false))
{
server.Start(CancellationToken.None);

await Get_NoPath_ShouldReturnIndexHtml(http);
await Get_IndexPath_ShouldReturnIndexHtml(http);
await Get_DirectoryPath_ShouldReturnDirectoryListing(http);
} // server should end when it is disposed
}

private static async Task Get_NoPath_ShouldReturnIndexHtml(HttpClient http)
{
var response = await http.GetAsync("http://localhost:8080/");
var content = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello World", content);
}

private static async Task Get_IndexPath_ShouldReturnIndexHtml(HttpClient http)
{
var response = await http.GetAsync("http://localhost:8080/index.html");
var content = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello World", content);
}

private static async Task Get_DirectoryPath_ShouldReturnDirectoryListing(HttpClient http)
{
var response = await http.GetAsync("http://localhost:8080/fruits/");
var content = await response.Content.ReadAsStringAsync();
Assert.Contains("Directory Listing", content);
Assert.Contains("apple", content);
Assert.Contains("pineapple", content);
Assert.Contains("tomato", content);
}
}
}
1 change: 1 addition & 0 deletions DevServer.Tests/IntegrationTests/WebRoot/fruits/apple.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
apples are crunchy and delicious
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pineapples are sweet and delicious
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tomatoes are of questionable fruit status
1 change: 1 addition & 0 deletions DevServer.Tests/IntegrationTests/WebRoot/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello World
90 changes: 90 additions & 0 deletions DevServer.Tests/RequestHandlerTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Abstractions.TestingHelpers;
using System.Net;
using Xunit;

namespace DevServer.Tests
{
public class RequestHandlerTests
{
private RequestHandler requestHandler;

public RequestHandlerTests()
{
var fs = new MockFileSystem(new Dictionary<string, MockFileData>
{
[@"C:\myserver\index.html"] = new MockFileData("Welcome to my blog"),
[@"C:\myserver\puppies.html"] = new MockFileData("this a page about my puppies"),
[@"C:\myserver\puppies\fido.jpg"] = new MockFileData(Array.Empty<byte>()),
[@"C:\myserver\puppies\boxer.jpg"] = new MockFileData(Array.Empty<byte>()),
[@"C:\myserver\puppies\pogo.jpg"] = new MockFileData(Array.Empty<byte>()),
[@"C:\private.txt"] = new MockFileData("the password is passw0rd"),
});
this.requestHandler = new RequestHandler(@"C:\myserver\", fs);
}

[Fact]
public void GenerateResponse_WithAbsolutePath_ReturnsContent()
{
var response = requestHandler.GenerateResponse("/index.html");
Assert.Equal("Welcome to my blog", new StreamReader(response.Body).ReadToEnd());
}

[Fact]
public void GenerateResponse_WithRelativePath_ReturnsContent()
{
var response = requestHandler.GenerateResponse("index.html");
Assert.Equal("Welcome to my blog", new StreamReader(response.Body).ReadToEnd());
}

[Fact]
public void GenerateResponse_WithBoundedDirectoryTraversal_EvaluatesTraversal()
{
// this traversal is ok, because it's still under the web root
var response = requestHandler.GenerateResponse("/puppies/../index.html");
Assert.Equal("Welcome to my blog", new StreamReader(response.Body).ReadToEnd());
}

[Fact]
public void GenerateResponse_WithEscapingDirectoryTraversal_DoesNotEvaluatesTraversal()
{
// this traversal is NOT ok, because it's trying to escape the web root.
var response = requestHandler.GenerateResponse("/../private.txt");
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}

[Fact]
public void GenerateResponse_WithHtmlFile_ReturnsHtmlContentType()
{
var response = requestHandler.GenerateResponse("/index.html");
Assert.Equal("text/html", response.ContentType);
}

[Fact]
public void GenerateResponse_WithJpegFile_ReturnsJpegContentType()
{
var response = requestHandler.GenerateResponse("/puppies/fido.jpg");
Assert.Equal("image/jpeg", response.ContentType);
}

[Fact]
public void GenerateResponse_NoFileName_ReturnsIndexHtml()
{
var response = requestHandler.GenerateResponse("/");
Assert.Equal("Welcome to my blog", new StreamReader(response.Body).ReadToEnd());
}

[Fact]
public void GenerateResponse_Directory_ReturnsDirectoryListing()
{
var response = requestHandler.GenerateResponse("/puppies/");
string content = new StreamReader(response.Body).ReadToEnd();

Assert.Contains("Directory Listing", content);
// we should assert that the listing has the three jpg files here, but this bug prevents it:
// https://github.com/System-IO-Abstractions/System.IO.Abstractions/issues/405
}
}
}
11 changes: 11 additions & 0 deletions DevServer.sln
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ VisualStudioVersion = 15.0.28010.2046
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevServer", "DevServer\DevServer.csproj", "{60F8FA24-06B6-4B42-BE40-5770A6BD0B25}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{679977D6-F2A6-40D6-ABD1-44C88AE70AC6}"
ProjectSection(SolutionItems) = preProject
README.md = README.md
EndProjectSection
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DevServer.Tests", "DevServer.Tests\DevServer.Tests.csproj", "{826A8050-6FF1-4DEA-9139-B715A6D42BFF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -15,6 +22,10 @@ Global
{60F8FA24-06B6-4B42-BE40-5770A6BD0B25}.Debug|Any CPU.Build.0 = Debug|Any CPU
{60F8FA24-06B6-4B42-BE40-5770A6BD0B25}.Release|Any CPU.ActiveCfg = Release|Any CPU
{60F8FA24-06B6-4B42-BE40-5770A6BD0B25}.Release|Any CPU.Build.0 = Release|Any CPU
{826A8050-6FF1-4DEA-9139-B715A6D42BFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{826A8050-6FF1-4DEA-9139-B715A6D42BFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{826A8050-6FF1-4DEA-9139-B715A6D42BFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{826A8050-6FF1-4DEA-9139-B715A6D42BFF}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
Loading

0 comments on commit 5565a42

Please sign in to comment.