mirror of
https://github.com/wiremock/WireMock.Net.git
synced 2026-03-21 16:48:59 +01:00
Improved relative path checking based on file existence (#411)
* Improved relative path checking based on file existence If the file exists at the relative path, then use it. If not, then use the path as is. * Apply File.Exists logic to ReadResponseBodyAsString as well * Make path handling more robust since path is user defined * Unit tests for relative path feature * Replace all back and forward slashes with system dependent DirectorySeparatorChar * Attempt fix broken directory separator chars for Unix platforms * Revert wrapping GetMappingFolder with CleanPath * Move CleanPath logic to its own class * Remove whitespace * Remove more whitespace * Improve CleanPath method * Move PathUtils tests to separate class Add another test to ResponseWithBodyFromFileTests * Fix Response_ProvideResponse_WithBodyFromFile_InAdminMappingFolder * Debug Linux CI build * Debug Linux CI * print all files from admin mappings folder * Debug CleanPath * Fix removed leading directory separator char in Linux breaks file path Remove debugging statements * Move combine to PathUtils * PathUtils + PathUtilsTests * Remove replicated (3x) tests throughout ResponseWithBodyFromFileTests Co-authored-by: Stef Heyenrath <Stef.Heyenrath@gmail.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using WireMock.Util;
|
||||
using WireMock.Validation;
|
||||
|
||||
namespace WireMock.Handlers
|
||||
@@ -80,20 +81,20 @@ namespace WireMock.Handlers
|
||||
public byte[] ReadResponseBodyAsFile(string path)
|
||||
{
|
||||
Check.NotNullOrEmpty(path, nameof(path));
|
||||
|
||||
// In case the path is a filename, the path will be adjusted to the MappingFolder.
|
||||
path = PathUtils.CleanPath(path);
|
||||
// If the file exists at the given path relative to the MappingsFolder, then return that.
|
||||
// Else the path will just be as-is.
|
||||
return File.ReadAllBytes(Path.GetFileName(path) == path ? Path.Combine(GetMappingFolder(), path) : path);
|
||||
return File.ReadAllBytes(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.ReadResponseBodyAsString"/>
|
||||
public string ReadResponseBodyAsString(string path)
|
||||
{
|
||||
Check.NotNullOrEmpty(path, nameof(path));
|
||||
|
||||
path = PathUtils.CleanPath(path);
|
||||
// In case the path is a filename, the path will be adjusted to the MappingFolder.
|
||||
// Else the path will just be as-is.
|
||||
return File.ReadAllText(Path.GetFileName(path) == path ? Path.Combine(GetMappingFolder(), path) : path);
|
||||
return File.ReadAllText(File.Exists(PathUtils.Combine(GetMappingFolder(), path)) ? PathUtils.Combine(GetMappingFolder(), path) : path);
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="IFileSystemHandler.FileExists"/>
|
||||
|
||||
36
src/WireMock.Net/Util/PathUtils.cs
Normal file
36
src/WireMock.Net/Util/PathUtils.cs
Normal file
@@ -0,0 +1,36 @@
|
||||
using System.IO;
|
||||
|
||||
namespace WireMock.Util
|
||||
{
|
||||
internal static class PathUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Robust handling of the user defined path.
|
||||
/// Also supports Unix and Windows platforms
|
||||
/// </summary>
|
||||
/// <param name="path">The path to clean</param>
|
||||
public static string CleanPath(string path)
|
||||
{
|
||||
return path?.Replace('/', Path.DirectorySeparatorChar).Replace('\\', Path.DirectorySeparatorChar);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes leading directory separator chars from the filepath, which could break Path.Combine
|
||||
/// </summary>
|
||||
/// <param name="path">The path to remove the loading DirectorySeparatorChars</param>
|
||||
public static string RemoveLeadingDirectorySeparators(string path)
|
||||
{
|
||||
return path?.TrimStart(new[] { Path.DirectorySeparatorChar });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combine two paths
|
||||
/// </summary>
|
||||
/// <param name="root">The root path</param>
|
||||
/// <param name="path">The path</param>
|
||||
public static string Combine(string root, string path)
|
||||
{
|
||||
return Path.Combine(root, RemoveLeadingDirectorySeparators(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
@@ -13,11 +15,10 @@ namespace WireMock.Net.Tests.ResponseBuilders
|
||||
public class ResponseWithBodyFromFileTests
|
||||
{
|
||||
[Fact]
|
||||
public async Task Response_ProvideResponse_WithBodyFromFile()
|
||||
public async Task Response_ProvideResponse_WithBodyFromFile_AbsolutePath()
|
||||
{
|
||||
// Arrange
|
||||
var server = WireMockServer.Start();
|
||||
|
||||
string path = Path.Combine(Directory.GetCurrentDirectory(), "__admin", "mappings", "MyXmlResponse.xml");
|
||||
|
||||
server
|
||||
@@ -36,14 +37,68 @@ namespace WireMock.Net.Tests.ResponseBuilders
|
||||
);
|
||||
|
||||
// Act
|
||||
var response1 = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
|
||||
var response2 = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
|
||||
var response3 = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
|
||||
var response = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
|
||||
|
||||
// Assert
|
||||
response1.Should().Contain("<hello>world</hello>");
|
||||
response2.Should().Contain("<hello>world</hello>");
|
||||
response3.Should().Contain("<hello>world</hello>");
|
||||
response.Should().Contain("<hello>world</hello>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Response_ProvideResponse_WithBodyFromFile_InSubDirectory()
|
||||
{
|
||||
// Arrange
|
||||
var server = WireMockServer.Start();
|
||||
string path = @"subdirectory/MyXmlResponse.xml";
|
||||
|
||||
server
|
||||
.Given(
|
||||
Request
|
||||
.Create()
|
||||
.UsingGet()
|
||||
.WithPath("/v1/content")
|
||||
)
|
||||
.RespondWith(
|
||||
Response
|
||||
.Create()
|
||||
.WithStatusCode(HttpStatusCode.OK)
|
||||
.WithHeader("Content-Type", "application/xml")
|
||||
.WithBodyFromFile(path)
|
||||
);
|
||||
|
||||
// Act
|
||||
var response = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
|
||||
|
||||
// Assert
|
||||
response.Should().Contain("<hello>world</hello>");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Response_ProvideResponse_WithBodyFromFile_InAdminMappingFolder()
|
||||
{
|
||||
// Arrange
|
||||
var server = WireMockServer.Start();
|
||||
string path = @"MyXmlResponse.xml";
|
||||
|
||||
server
|
||||
.Given(
|
||||
Request
|
||||
.Create()
|
||||
.UsingGet()
|
||||
.WithPath("/v1/content")
|
||||
)
|
||||
.RespondWith(
|
||||
Response
|
||||
.Create()
|
||||
.WithStatusCode(HttpStatusCode.OK)
|
||||
.WithHeader("Content-Type", "application/xml")
|
||||
.WithBodyFromFile(path)
|
||||
);
|
||||
|
||||
// Act
|
||||
var response = await new HttpClient().GetStringAsync("http://localhost:" + server.Ports[0] + "/v1/content");
|
||||
|
||||
// Assert
|
||||
response.Should().Contain("<hello>world</hello>");
|
||||
}
|
||||
}
|
||||
}
|
||||
44
test/WireMock.Net.Tests/Util/PathUtilsTests.cs
Normal file
44
test/WireMock.Net.Tests/Util/PathUtilsTests.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using NFluent;
|
||||
using System.IO;
|
||||
using WireMock.Util;
|
||||
using Xunit;
|
||||
|
||||
namespace WireMock.Net.Tests.Util
|
||||
{
|
||||
public class PathUtilsTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(@"subdirectory/MyXmlResponse.xml")]
|
||||
[InlineData(@"subdirectory\MyXmlResponse.xml")]
|
||||
public void PathUtils_CleanPath(string path)
|
||||
{
|
||||
// Act
|
||||
var cleanPath = PathUtils.CleanPath(path);
|
||||
|
||||
// Assert
|
||||
Check.That(cleanPath).Equals("subdirectory" + Path.DirectorySeparatorChar + "MyXmlResponse.xml");
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(null, null)]
|
||||
[InlineData("", "")]
|
||||
[InlineData("a", "a")]
|
||||
[InlineData(@"/", "")]
|
||||
[InlineData(@"//", "")]
|
||||
[InlineData(@"//a", "a")]
|
||||
[InlineData(@"\", "")]
|
||||
[InlineData(@"\\", "")]
|
||||
[InlineData(@"\\a", "a")]
|
||||
public void PathUtils_CleanPath_RemoveLeadingDirectorySeparators(string path, string expected)
|
||||
{
|
||||
// Arrange
|
||||
var cleanPath = PathUtils.CleanPath(path);
|
||||
|
||||
// Act
|
||||
var withoutDirectorySeparators = PathUtils.RemoveLeadingDirectorySeparators(cleanPath);
|
||||
|
||||
// Assert
|
||||
Check.That(withoutDirectorySeparators).Equals(expected);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -83,6 +83,9 @@
|
||||
<None Update="__admin\mappings\MyXmlResponse.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="__admin\mappings\subdirectory\MyXmlResponse.xml">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<xml>
|
||||
<hello>world</hello>
|
||||
</xml>
|
||||
Reference in New Issue
Block a user