diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3da785c5..12fc69db 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,17 @@
+# 1.0.15.0 (04 May 2019)
+- [#271](https://github.com/WireMock-Net/WireMock.Net/pull/271) - Support Dynamic response files using Handlebars templating [bug, feature] contributed by [StefH](https://github.com/StefH)
+- [#272](https://github.com/WireMock-Net/WireMock.Net/pull/272) - Add unit test for JsonPath and BodyAsFile mapping contributed by [denstorti](https://github.com/denstorti)
+- [#273](https://github.com/WireMock-Net/WireMock.Net/pull/273) - Dynamic response handlebars templating (2) [bug, feature] contributed by [StefH](https://github.com/StefH)
+- [#270](https://github.com/WireMock-Net/WireMock.Net/issues/270) - Dynamic response files using Handlebars templating [bug, question]
+
+# 1.0.14.0 (20 April 2019)
+- [#269](https://github.com/WireMock-Net/WireMock.Net/pull/269) - Add JmesPath matcher [feature] contributed by [StefH](https://github.com/StefH)
+- [#268](https://github.com/WireMock-Net/WireMock.Net/issues/268) - Swagger UI for admin api [question]
+
+# 1.0.13.0 (11 April 2019)
+- [#266](https://github.com/WireMock-Net/WireMock.Net/pull/266) - [265] Add file upload to allow mocking of file operations contributed by [JackCreativeCrew](https://github.com/JackCreativeCrew)
+- [#265](https://github.com/WireMock-Net/WireMock.Net/issues/265) - File Upload [feature]
+
# 1.0.12.0 (05 April 2019)
- [#264](https://github.com/WireMock-Net/WireMock.Net/pull/264) - Proxy : also save multipart as string in mapping file contributed by [StefH](https://github.com/StefH)
- [#263](https://github.com/WireMock-Net/WireMock.Net/issues/263) - Content-Type multipart/form-data is not serialized in proxy and recording mode [bug]
diff --git a/Directory.Build.props b/Directory.Build.props
index 8031a4fb..085afa88 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -4,7 +4,7 @@
- 1.0.12
+ 1.0.15
diff --git a/GitHubReleaseNotes.txt b/GitHubReleaseNotes.txt
index fa63df65..75e562c1 100644
--- a/GitHubReleaseNotes.txt
+++ b/GitHubReleaseNotes.txt
@@ -1,3 +1,3 @@
https://github.com/StefH/GitHubReleaseNotes
-GitHubReleaseNotes.exe --output CHANGELOG.md --skip-empty-releases --version 1.0.11.0
\ No newline at end of file
+GitHubReleaseNotes.exe --output CHANGELOG.md --skip-empty-releases --version 1.0.15.0
\ No newline at end of file
diff --git a/README.md b/README.md
index 82988722..bfa2eae5 100644
--- a/README.md
+++ b/README.md
@@ -8,7 +8,7 @@ A C# .NET version based on [mock4net](https://github.com/alexvictoor/mock4net) w
* Record/playback of stubs (proxying)
* Per-request conditional proxying
* Stateful behaviour simulation
-* Response transformation
+* Response templating / transformation using Handlebars and extensions
## Info
| | |
diff --git a/WireMock.Net Solution.sln.DotSettings b/WireMock.Net Solution.sln.DotSettings
index 4556d123..8f9aa5d2 100644
--- a/WireMock.Net Solution.sln.DotSettings
+++ b/WireMock.Net Solution.sln.DotSettings
@@ -10,6 +10,7 @@
XUA
True
True
+ True
True
True
\ No newline at end of file
diff --git a/examples/WireMock.Net.Client/Program.cs b/examples/WireMock.Net.Client/Program.cs
index 560195f0..4a8dabfa 100644
--- a/examples/WireMock.Net.Client/Program.cs
+++ b/examples/WireMock.Net.Client/Program.cs
@@ -52,6 +52,12 @@ namespace WireMock.Net.Client
var scenarioStates = api.GetScenariosAsync().Result;
Console.WriteLine($"GetScenariosAsync = {JsonConvert.SerializeObject(scenarioStates)}");
+ var postFileResult = api.PostFileAsync("1.cs", "C# Hello").GetAwaiter().GetResult();
+ Console.WriteLine($"postFileResult = {JsonConvert.SerializeObject(postFileResult)}");
+
+ var getFileResult = api.GetFileAsync("1.cs").GetAwaiter().GetResult();
+ Console.WriteLine($"getFileResult = {getFileResult}");
+
Console.WriteLine("Press any key to quit");
Console.ReadKey();
}
diff --git a/examples/WireMock.Net.Console.Net452.Classic/CustomFileSystemFileHandler.cs b/examples/WireMock.Net.Console.Net452.Classic/CustomFileSystemFileHandler.cs
index 0c558479..fcad7619 100644
--- a/examples/WireMock.Net.Console.Net452.Classic/CustomFileSystemFileHandler.cs
+++ b/examples/WireMock.Net.Console.Net452.Classic/CustomFileSystemFileHandler.cs
@@ -49,5 +49,39 @@ namespace WireMock.Net.ConsoleApplication
{
return File.ReadAllBytes(Path.GetFileName(path) == path ? Path.Combine(GetMappingFolder(), path) : path);
}
+
+ ///
+ public bool FileExists(string path)
+ {
+ return File.Exists(AdjustPath(path));
+ }
+
+ ///
+ public void WriteFile(string path, byte[] bytes)
+ {
+ File.WriteAllBytes(AdjustPath(path), bytes);
+ }
+
+ ///
+ public void DeleteFile(string path)
+ {
+ File.Delete(AdjustPath(path));
+ }
+
+ ///
+ public byte[] ReadFile(string path)
+ {
+ return File.ReadAllBytes(AdjustPath(path));
+ }
+
+ ///
+ /// Adjusts the path to the MappingFolder.
+ ///
+ /// The path.
+ /// Adjusted path
+ private string AdjustPath(string path)
+ {
+ return Path.Combine(GetMappingFolder(), path);
+ }
}
}
\ No newline at end of file
diff --git a/examples/WireMock.Net.StandAlone.Net461/App.config b/examples/WireMock.Net.StandAlone.Net461/App.config
index 4be9c6ef..0b11f8d0 100644
--- a/examples/WireMock.Net.StandAlone.Net461/App.config
+++ b/examples/WireMock.Net.StandAlone.Net461/App.config
@@ -44,7 +44,7 @@
-
+
diff --git a/examples/WireMock.Net.StandAlone.Net461/WireMock.Net.StandAlone.Net461.csproj b/examples/WireMock.Net.StandAlone.Net461/WireMock.Net.StandAlone.Net461.csproj
index cc1988ce..4a9309c9 100644
--- a/examples/WireMock.Net.StandAlone.Net461/WireMock.Net.StandAlone.Net461.csproj
+++ b/examples/WireMock.Net.StandAlone.Net461/WireMock.Net.StandAlone.Net461.csproj
@@ -225,8 +225,8 @@
..\..\packages\System.IO.Pipelines.4.5.2\lib\netstandard2.0\System.IO.Pipelines.dll
-
- ..\..\packages\System.Linq.Dynamic.Core.1.0.11\lib\net46\System.Linq.Dynamic.Core.dll
+
+ ..\..\packages\System.Linq.Dynamic.Core.1.0.12\lib\net46\System.Linq.Dynamic.Core.dll
..\..\packages\System.Memory.4.5.1\lib\netstandard2.0\System.Memory.dll
diff --git a/examples/WireMock.Net.StandAlone.Net461/packages.config b/examples/WireMock.Net.StandAlone.Net461/packages.config
index 9ff4575a..679c143c 100644
--- a/examples/WireMock.Net.StandAlone.Net461/packages.config
+++ b/examples/WireMock.Net.StandAlone.Net461/packages.config
@@ -65,7 +65,7 @@
-
+
diff --git a/src/WireMock.Net/Client/IFluentMockServerAdmin.cs b/src/WireMock.Net/Client/IFluentMockServerAdmin.cs
index 430dfa4f..bddaf37f 100644
--- a/src/WireMock.Net/Client/IFluentMockServerAdmin.cs
+++ b/src/WireMock.Net/Client/IFluentMockServerAdmin.cs
@@ -167,5 +167,42 @@ namespace WireMock.Client
///
[Post("__admin/scenarios")]
Task ResetScenariosAsync();
+
+ ///
+ /// Create a new File
+ ///
+ /// The filename
+ /// The body
+ [Post("__admin/files/{filename}")]
+ Task PostFileAsync([Path] string filename, [Body] string body);
+
+ ///
+ /// Update an existing File
+ ///
+ /// The filename
+ /// The body
+ [Put("__admin/files/{filename}")]
+ Task PutFileAsync([Path] string filename, [Body] string body);
+
+ ///
+ /// Get the content of an existing File
+ ///
+ /// The filename
+ [Get("__admin/files/{filename}")]
+ Task GetFileAsync([Path] string filename);
+
+ ///
+ /// Delete an existing File
+ ///
+ /// The filename
+ [Delete("__admin/files/{filename}")]
+ Task DeleteFileAsync([Path] string filename);
+
+ ///
+ /// Check if a file exists
+ ///
+ /// The filename
+ [Head("__admin/files/{filename}")]
+ Task FileExistsAsync([Path] string filename);
}
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Handlers/IFileSystemHandler.cs b/src/WireMock.Net/Handlers/IFileSystemHandler.cs
index 15268735..5ef5ef5c 100644
--- a/src/WireMock.Net/Handlers/IFileSystemHandler.cs
+++ b/src/WireMock.Net/Handlers/IFileSystemHandler.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using JetBrains.Annotations;
+using System.Collections.Generic;
namespace WireMock.Handlers
{
@@ -18,40 +19,67 @@ namespace WireMock.Handlers
///
/// The path.
/// true if path refers to an existing directory; false if the directory does not exist or an error occurs when trying to determine if the specified directory exists.
- bool FolderExists(string path);
+ bool FolderExists([NotNull] string path);
///
/// Creates all directories and subdirectories in the specified path unless they already exist.
///
/// The path.
- void CreateFolder(string path);
+ void CreateFolder([NotNull] string path);
///
/// Returns an enumerable collection of file names in a specified path.
///
/// The path.
/// An enumerable collection of the full names (including paths) for the files in the directory specified by path.
- IEnumerable EnumerateFiles(string path);
+ IEnumerable EnumerateFiles([NotNull] string path);
///
/// Read a static mapping file as text.
///
/// The path (folder + filename with .json extension).
/// The file content as text.
- string ReadMappingFile(string path);
+ string ReadMappingFile([NotNull] string path);
///
- /// Write the static mapping.
+ /// Write the static mapping file.
///
/// The path (folder + filename with .json extension).
/// The text.
- void WriteMappingFile(string path, string text);
+ void WriteMappingFile([NotNull] string path, [NotNull] string text);
///
/// Read a response body file as text.
///
/// The path or filename from the file to read.
/// The file content as bytes.
- byte[] ReadResponseBodyAsFile(string path);
+ byte[] ReadResponseBodyAsFile([NotNull] string path);
+
+ ///
+ /// Delete a file.
+ ///
+ /// The filename.
+ void DeleteFile([NotNull] string filename);
+
+ ///
+ /// Determines whether the given path refers to an existing file on disk.
+ ///
+ /// The filename.
+ /// true if path refers to an existing file; false if the file does not exist.
+ bool FileExists([NotNull] string filename);
+
+ ///
+ /// Write a file.
+ ///
+ /// The filename.
+ /// The bytes.
+ void WriteFile([NotNull] string filename, [NotNull] byte[] bytes);
+
+ ///
+ /// Read a file as bytes.
+ ///
+ /// The filename.
+ /// The file content as bytes.
+ byte[] ReadFile([NotNull] string filename);
}
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Handlers/LocalFileSystemHandler.cs b/src/WireMock.Net/Handlers/LocalFileSystemHandler.cs
index bfc7007a..d70bdc53 100644
--- a/src/WireMock.Net/Handlers/LocalFileSystemHandler.cs
+++ b/src/WireMock.Net/Handlers/LocalFileSystemHandler.cs
@@ -1,5 +1,4 @@
-using JetBrains.Annotations;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.IO;
using WireMock.Validation;
@@ -13,7 +12,7 @@ namespace WireMock.Handlers
private static readonly string AdminMappingsFolder = Path.Combine("__admin", "mappings");
///
- public bool FolderExists([NotNull] string path)
+ public bool FolderExists(string path)
{
Check.NotNullOrEmpty(path, nameof(path));
@@ -21,7 +20,7 @@ namespace WireMock.Handlers
}
///
- public void CreateFolder([NotNull] string path)
+ public void CreateFolder(string path)
{
Check.NotNullOrEmpty(path, nameof(path));
@@ -29,7 +28,7 @@ namespace WireMock.Handlers
}
///
- public IEnumerable EnumerateFiles([NotNull] string path)
+ public IEnumerable EnumerateFiles(string path)
{
Check.NotNullOrEmpty(path, nameof(path));
@@ -43,15 +42,15 @@ namespace WireMock.Handlers
}
///
- public string ReadMappingFile([NotNull] string path)
+ public string ReadMappingFile(string path)
{
Check.NotNullOrEmpty(path, nameof(path));
return File.ReadAllText(path);
}
- ///
- public void WriteMappingFile([NotNull] string path, [NotNull] string text)
+ ///
+ public void WriteMappingFile(string path, string text)
{
Check.NotNullOrEmpty(path, nameof(path));
Check.NotNull(text, nameof(text));
@@ -68,5 +67,48 @@ namespace WireMock.Handlers
// Else the path will just be as-is.
return File.ReadAllBytes(Path.GetFileName(path) == path ? Path.Combine(GetMappingFolder(), path) : path);
}
+
+ ///
+ public bool FileExists(string filename)
+ {
+ Check.NotNullOrEmpty(filename, nameof(filename));
+
+ return File.Exists(AdjustPath(filename));
+ }
+
+ ///
+ public void WriteFile(string filename, byte[] bytes)
+ {
+ Check.NotNullOrEmpty(filename, nameof(filename));
+ Check.NotNull(bytes, nameof(bytes));
+
+ File.WriteAllBytes(AdjustPath(filename), bytes);
+ }
+
+ ///
+ public void DeleteFile(string filename)
+ {
+ Check.NotNullOrEmpty(filename, nameof(filename));
+
+ File.Delete(AdjustPath(filename));
+ }
+
+ ///
+ public byte[] ReadFile(string filename)
+ {
+ Check.NotNullOrEmpty(filename, nameof(filename));
+
+ return File.ReadAllBytes(AdjustPath(filename));
+ }
+
+ ///
+ /// Adjusts the path to the MappingFolder.
+ ///
+ /// The path.
+ /// Adjusted path
+ private string AdjustPath(string filename)
+ {
+ return Path.Combine(GetMappingFolder(), filename);
+ }
}
}
diff --git a/src/WireMock.Net/Matchers/JmesPathMatcher.cs b/src/WireMock.Net/Matchers/JmesPathMatcher.cs
new file mode 100644
index 00000000..a01308f0
--- /dev/null
+++ b/src/WireMock.Net/Matchers/JmesPathMatcher.cs
@@ -0,0 +1,83 @@
+using DevLab.JmesPath;
+using JetBrains.Annotations;
+using Newtonsoft.Json;
+using System.Linq;
+using WireMock.Validation;
+
+namespace WireMock.Matchers
+{
+ ///
+ /// http://jmespath.org/
+ ///
+ public class JmesPathMatcher : IStringMatcher, IObjectMatcher
+ {
+ private readonly string[] _patterns;
+
+ ///
+ public MatchBehaviour MatchBehaviour { get; }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The patterns.
+ public JmesPathMatcher([NotNull] params string[] patterns) : this(MatchBehaviour.AcceptOnMatch, patterns)
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The match behaviour.
+ /// The patterns.
+ public JmesPathMatcher(MatchBehaviour matchBehaviour, [NotNull] params string[] patterns)
+ {
+ Check.NotNull(patterns, nameof(patterns));
+
+ MatchBehaviour = matchBehaviour;
+ _patterns = patterns;
+ }
+
+ ///
+ public double IsMatch(string input)
+ {
+ double match = MatchScores.Mismatch;
+ if (input != null)
+ {
+ try
+ {
+ match = MatchScores.ToScore(_patterns.Select(pattern => bool.Parse(new JmesPath().Transform(input, pattern))));
+ }
+ catch (JsonException)
+ {
+ // just ignore JsonException
+ }
+ }
+
+ return MatchBehaviourHelper.Convert(MatchBehaviour, match);
+ }
+
+ ///
+ public double IsMatch(object input)
+ {
+ double match = MatchScores.Mismatch;
+
+ // When input is null or byte[], return Mismatch.
+ if (input != null && !(input is byte[]))
+ {
+ string inputAsString = JsonConvert.SerializeObject(input);
+ return IsMatch(inputAsString);
+ }
+
+ return MatchBehaviourHelper.Convert(MatchBehaviour, match);
+ }
+
+ ///
+ public string[] GetPatterns()
+ {
+ return _patterns;
+ }
+
+ ///
+ public string Name => "JmesPathMatcher";
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net/ResponseBuilders/Response.cs b/src/WireMock.Net/ResponseBuilders/Response.cs
index 3df50690..82ba0852 100644
--- a/src/WireMock.Net/ResponseBuilders/Response.cs
+++ b/src/WireMock.Net/ResponseBuilders/Response.cs
@@ -224,7 +224,7 @@ namespace WireMock.ResponseBuilders
BodyAsFileIsCached = cache
};
- if (cache)
+ if (cache && !UseTransformer)
{
ResponseMessage.BodyData.DetectedBodyType = BodyType.Bytes;
ResponseMessage.BodyData.BodyAsBytes = _fileSystemHandler.ReadResponseBodyAsFile(filename);
diff --git a/src/WireMock.Net/ResponseMessageBuilder.cs b/src/WireMock.Net/ResponseMessageBuilder.cs
index 7a14ffd9..a6d85bb5 100644
--- a/src/WireMock.Net/ResponseMessageBuilder.cs
+++ b/src/WireMock.Net/ResponseMessageBuilder.cs
@@ -33,5 +33,13 @@ namespace WireMock
return response;
}
+
+ internal static ResponseMessage Create(int statusCode)
+ {
+ return new ResponseMessage
+ {
+ StatusCode = statusCode
+ };
+ }
}
}
\ No newline at end of file
diff --git a/src/WireMock.Net/Serialization/MappingConverter.cs b/src/WireMock.Net/Serialization/MappingConverter.cs
index 4cc45e75..5f848b48 100644
--- a/src/WireMock.Net/Serialization/MappingConverter.cs
+++ b/src/WireMock.Net/Serialization/MappingConverter.cs
@@ -77,7 +77,7 @@ namespace WireMock.Serialization
},
Response = new ResponseModel
{
- Delay = (int?) response.Delay?.TotalMilliseconds
+ Delay = (int?)response.Delay?.TotalMilliseconds
}
};
diff --git a/src/WireMock.Net/Serialization/MatcherMapper.cs b/src/WireMock.Net/Serialization/MatcherMapper.cs
index 57b128e5..14154c7f 100644
--- a/src/WireMock.Net/Serialization/MatcherMapper.cs
+++ b/src/WireMock.Net/Serialization/MatcherMapper.cs
@@ -45,6 +45,9 @@ namespace WireMock.Serialization
case "JsonPathMatcher":
return new JsonPathMatcher(matchBehaviour, stringPatterns);
+ case "JmesPathMatcher":
+ return new JmesPathMatcher(matchBehaviour, stringPatterns);
+
case "XPathMatcher":
return new XPathMatcher(matchBehaviour, (string)matcher.Pattern);
diff --git a/src/WireMock.Net/Server/FluentMockServer.Admin.cs b/src/WireMock.Net/Server/FluentMockServer.Admin.cs
index 4919d031..1da20c3c 100644
--- a/src/WireMock.Net/Server/FluentMockServer.Admin.cs
+++ b/src/WireMock.Net/Server/FluentMockServer.Admin.cs
@@ -34,13 +34,14 @@ namespace WireMock.Server
private const int AdminPriority = int.MinValue;
private const int ProxyPriority = 1000;
private const string ContentTypeJson = "application/json";
+ private const string AdminFiles = "/__admin/files";
private const string AdminMappings = "/__admin/mappings";
private const string AdminRequests = "/__admin/requests";
private const string AdminSettings = "/__admin/settings";
private const string AdminScenarios = "/__admin/scenarios";
- private readonly RegexMatcher _adminMappingsGuidPathMatcher = new RegexMatcher(MatchBehaviour.AcceptOnMatch, @"^\/__admin\/mappings\/(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$");
- private readonly RegexMatcher _adminRequestsGuidPathMatcher = new RegexMatcher(MatchBehaviour.AcceptOnMatch, @"^\/__admin\/requests\/(\{{0,1}([0-9a-fA-F]){8}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){4}-([0-9a-fA-F]){12}\}{0,1})$");
+ private readonly RegexMatcher _adminMappingsGuidPathMatcher = new RegexMatcher(MatchBehaviour.AcceptOnMatch, @"^\/__admin\/mappings\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
+ private readonly RegexMatcher _adminRequestsGuidPathMatcher = new RegexMatcher(MatchBehaviour.AcceptOnMatch, @"^\/__admin\/requests\/([0-9A-Fa-f]{8}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{4}[-][0-9A-Fa-f]{12})$");
private readonly JsonSerializerSettings _settings = new JsonSerializerSettings
{
@@ -97,10 +98,17 @@ namespace WireMock.Server
// __admin/scenarios/reset
Given(Request.Create().WithPath(AdminScenarios + "/reset").UsingPost()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(ScenariosReset));
+
+ // __admin/files/{filename}
+ Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingPost()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(FilePost));
+ Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingPut()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(FilePut));
+ Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingGet()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(FileGet));
+ Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingHead()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(FileHead));
+ Given(Request.Create().WithPath(_adminFilesFilenamePathMatcher).UsingDelete()).AtPriority(AdminPriority).RespondWith(new DynamicResponseProvider(FileDelete));
}
#endregion
- #region StaticMappings
+ #region StaticMappings
///
/// Saves the static mappings.
///
@@ -714,6 +722,11 @@ namespace WireMock.Server
responseBuilder = responseBuilder.WithDelay(responseModel.Delay.Value);
}
+ if (responseModel.UseTransformer)
+ {
+ responseBuilder = responseBuilder.WithTransformer();
+ }
+
if (!string.IsNullOrEmpty(responseModel.ProxyUrl))
{
if (string.IsNullOrEmpty(responseModel.X509Certificate2ThumbprintOrSubjectName))
@@ -770,11 +783,6 @@ namespace WireMock.Server
responseBuilder = responseBuilder.WithBodyFromFile(responseModel.BodyAsFile);
}
- if (responseModel.UseTransformer)
- {
- responseBuilder = responseBuilder.WithTransformer();
- }
-
return responseBuilder;
}
diff --git a/src/WireMock.Net/Server/FluentMockServer.AdminFiles.cs b/src/WireMock.Net/Server/FluentMockServer.AdminFiles.cs
new file mode 100644
index 00000000..5c72015a
--- /dev/null
+++ b/src/WireMock.Net/Server/FluentMockServer.AdminFiles.cs
@@ -0,0 +1,114 @@
+using System.IO;
+using System.Linq;
+using System.Text;
+using WireMock.Matchers;
+using WireMock.Util;
+
+namespace WireMock.Server
+{
+ public partial class FluentMockServer
+ {
+ private readonly RegexMatcher _adminFilesFilenamePathMatcher = new RegexMatcher(MatchBehaviour.AcceptOnMatch, @"^\/__admin\/files\/.*$");
+ private static readonly Encoding[] FileBodyIsString = { Encoding.UTF8, Encoding.ASCII };
+
+ #region Files/{filename}
+ private ResponseMessage FilePost(RequestMessage requestMessage)
+ {
+ string filename = GetFileNameFromRequestMessage(requestMessage);
+
+ string mappingFolder = _fileSystemHandler.GetMappingFolder();
+ if (!_fileSystemHandler.FolderExists(mappingFolder))
+ {
+ _fileSystemHandler.CreateFolder(mappingFolder);
+ }
+
+ _fileSystemHandler.WriteFile(filename, requestMessage.BodyAsBytes);
+
+ return ResponseMessageBuilder.Create("File created");
+ }
+
+ private ResponseMessage FilePut(RequestMessage requestMessage)
+ {
+ string filename = GetFileNameFromRequestMessage(requestMessage);
+
+ if (!_fileSystemHandler.FileExists(filename))
+ {
+ _logger.Info("The file '{0}' does not exist, updating file will be skipped.", filename);
+ return ResponseMessageBuilder.Create("File is not found", 404);
+ }
+
+ _fileSystemHandler.WriteFile(filename, requestMessage.BodyAsBytes);
+
+ return ResponseMessageBuilder.Create("File updated");
+ }
+
+ private ResponseMessage FileGet(RequestMessage requestMessage)
+ {
+ string filename = GetFileNameFromRequestMessage(requestMessage);
+
+ if (!_fileSystemHandler.FileExists(filename))
+ {
+ _logger.Info("The file '{0}' does not exist.", filename);
+ return ResponseMessageBuilder.Create("File is not found", 404);
+ }
+
+ byte[] bytes = _fileSystemHandler.ReadFile(filename);
+ var response = new ResponseMessage
+ {
+ StatusCode = 200,
+ BodyData = new BodyData
+ {
+ BodyAsBytes = bytes,
+ DetectedBodyType = BodyType.Bytes,
+ DetectedBodyTypeFromContentType = BodyType.None
+ }
+ };
+
+ if (BytesEncodingUtils.TryGetEncoding(bytes, out Encoding encoding) && FileBodyIsString.Select(x => x.Equals(encoding)).Any())
+ {
+ response.BodyData.DetectedBodyType = BodyType.String;
+ response.BodyData.BodyAsString = encoding.GetString(bytes);
+ }
+
+ return response;
+ }
+
+ ///
+ /// Checks if file exists.
+ /// Note: Response is returned with no body as a head request doesn't accept a body, only the status code.
+ ///
+ /// The request message.
+ private ResponseMessage FileHead(RequestMessage requestMessage)
+ {
+ string filename = GetFileNameFromRequestMessage(requestMessage);
+
+ if (!_fileSystemHandler.FileExists(filename))
+ {
+ _logger.Info("The file '{0}' does not exist.", filename);
+ return ResponseMessageBuilder.Create(404);
+ }
+
+ return ResponseMessageBuilder.Create(204);
+ }
+
+ private ResponseMessage FileDelete(RequestMessage requestMessage)
+ {
+ string filename = GetFileNameFromRequestMessage(requestMessage);
+
+ if (!_fileSystemHandler.FileExists(filename))
+ {
+ _logger.Info("The file '{0}' does not exist.", filename);
+ return ResponseMessageBuilder.Create("File is not deleted", 404);
+ }
+
+ _fileSystemHandler.DeleteFile(filename);
+ return ResponseMessageBuilder.Create("File deleted.");
+ }
+
+ private string GetFileNameFromRequestMessage(RequestMessage requestMessage)
+ {
+ return Path.GetFileName(requestMessage.Path.Substring(AdminFiles.Length + 1));
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/WireMock.Net/Transformers/ResponseMessageTransformer.cs b/src/WireMock.Net/Transformers/ResponseMessageTransformer.cs
index cdd8ae82..e2c742dc 100644
--- a/src/WireMock.Net/Transformers/ResponseMessageTransformer.cs
+++ b/src/WireMock.Net/Transformers/ResponseMessageTransformer.cs
@@ -1,9 +1,9 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using HandlebarsDotNet;
+using HandlebarsDotNet;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
+using System;
+using System.Collections.Generic;
+using System.Linq;
using WireMock.Util;
namespace WireMock.Transformers
@@ -24,23 +24,24 @@ namespace WireMock.Transformers
public static ResponseMessage Transform(RequestMessage requestMessage, ResponseMessage original)
{
- bool bodyIsJson = original.BodyData.DetectedBodyType == BodyType.Json;
var responseMessage = new ResponseMessage { StatusCode = original.StatusCode };
- if (!bodyIsJson)
- {
- responseMessage.BodyOriginal = original.BodyData.BodyAsString;
- }
-
var template = new { request = requestMessage };
- if (!bodyIsJson)
+ switch (original.BodyData.DetectedBodyType)
{
- TransformBodyAsString(template, original, responseMessage);
- }
- else
- {
- TransformBodyAsJson(template, original, responseMessage);
+ case BodyType.Json:
+ TransformBodyAsJson(template, original, responseMessage);
+ break;
+
+ case BodyType.File:
+ TransformBodyAsFile(template, original, responseMessage);
+ break;
+
+ case BodyType.String:
+ responseMessage.BodyOriginal = original.BodyData.BodyAsString;
+ TransformBodyAsString(template, original, responseMessage);
+ break;
}
// Headers
@@ -150,13 +151,25 @@ namespace WireMock.Transformers
private static void TransformBodyAsString(object template, ResponseMessage original, ResponseMessage responseMessage)
{
- var templateBody = HandlebarsContext.Compile(original.BodyData.BodyAsString);
+ var templateBodyAsString = HandlebarsContext.Compile(original.BodyData.BodyAsString);
responseMessage.BodyData = new BodyData
{
DetectedBodyType = original.BodyData.DetectedBodyType,
DetectedBodyTypeFromContentType = original.BodyData.DetectedBodyTypeFromContentType,
- BodyAsString = templateBody(template)
+ BodyAsString = templateBodyAsString(template)
+ };
+ }
+
+ private static void TransformBodyAsFile(object template, ResponseMessage original, ResponseMessage responseMessage)
+ {
+ var templateBodyAsFile = HandlebarsContext.Compile(original.BodyData.BodyAsFile);
+
+ responseMessage.BodyData = new BodyData
+ {
+ DetectedBodyType = original.BodyData.DetectedBodyType,
+ DetectedBodyTypeFromContentType = original.BodyData.DetectedBodyTypeFromContentType,
+ BodyAsFile = templateBodyAsFile(template)
};
}
}
diff --git a/src/WireMock.Net/WireMock.Net.csproj b/src/WireMock.Net/WireMock.Net.csproj
index 6b405481..f8e012e4 100644
--- a/src/WireMock.Net/WireMock.Net.csproj
+++ b/src/WireMock.Net/WireMock.Net.csproj
@@ -57,8 +57,9 @@
all
runtime; build; native; contentfiles; analyzers
-
+
+
diff --git a/test/WireMock.Net.Tests/FLuentMockServerTests.AdminFiles.cs b/test/WireMock.Net.Tests/FLuentMockServerTests.AdminFiles.cs
new file mode 100644
index 00000000..4d362d4a
--- /dev/null
+++ b/test/WireMock.Net.Tests/FLuentMockServerTests.AdminFiles.cs
@@ -0,0 +1,215 @@
+using Moq;
+using NFluent;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Net.Http;
+using System.Net.Http.Headers;
+using System.Text;
+using System.Threading.Tasks;
+using WireMock.Handlers;
+using WireMock.Server;
+using WireMock.Settings;
+using Xunit;
+
+namespace WireMock.Net.Tests
+{
+ public class FluentMockServerAdminFilesTests
+ {
+ private readonly HttpClient _client = new HttpClient();
+
+ [Fact]
+ public async Task FluentMockServer_Admin_Files_Post_Ascii()
+ {
+ // Arrange
+ var filesystemHandlerMock = new Mock(MockBehavior.Strict);
+ filesystemHandlerMock.Setup(fs => fs.GetMappingFolder()).Returns("__admin/mappings");
+ filesystemHandlerMock.Setup(fs => fs.FolderExists(It.IsAny())).Returns(true);
+ filesystemHandlerMock.Setup(fs => fs.WriteFile(It.IsAny(), It.IsAny()));
+
+ var server = FluentMockServer.Start(new FluentMockServerSettings
+ {
+ UseSSL = false,
+ StartAdminInterface = true,
+ FileSystemHandler = filesystemHandlerMock.Object
+ });
+
+ var multipartFormDataContent = new MultipartFormDataContent();
+ multipartFormDataContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
+ multipartFormDataContent.Add(new StreamContent(new MemoryStream(Encoding.ASCII.GetBytes("Here's a string."))));
+
+ // Act
+ var httpResponseMessage = await _client.PostAsync("http://localhost:" + server.Ports[0] + "/__admin/files/filename.txt", multipartFormDataContent);
+
+ // Assert
+ Check.That(httpResponseMessage.StatusCode).Equals(HttpStatusCode.OK);
+ Check.That(server.LogEntries.Count().Equals(1));
+
+ // Verify
+ filesystemHandlerMock.Verify(fs => fs.GetMappingFolder(), Times.Once);
+ filesystemHandlerMock.Verify(fs => fs.FolderExists(It.IsAny()), Times.Once);
+ filesystemHandlerMock.Verify(fs => fs.WriteFile(It.Is(p => p == "filename.txt"), It.IsAny()), Times.Once);
+ filesystemHandlerMock.VerifyNoOtherCalls();
+ }
+
+ [Fact]
+ public async Task FluentMockServer_Admin_Files_Post_MappingFolderDoesNotExistsButWillBeCreated()
+ {
+ // Arrange
+ var filesystemHandlerMock = new Mock(MockBehavior.Strict);
+ filesystemHandlerMock.Setup(fs => fs.GetMappingFolder()).Returns("x");
+ filesystemHandlerMock.Setup(fs => fs.CreateFolder(It.IsAny()));
+ filesystemHandlerMock.Setup(fs => fs.FolderExists(It.IsAny())).Returns(false);
+ filesystemHandlerMock.Setup(fs => fs.WriteFile(It.IsAny(), It.IsAny()));
+
+ var server = FluentMockServer.Start(new FluentMockServerSettings
+ {
+ UseSSL = false,
+ StartAdminInterface = true,
+ FileSystemHandler = filesystemHandlerMock.Object
+ });
+
+ var multipartFormDataContent = new MultipartFormDataContent();
+ multipartFormDataContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
+ multipartFormDataContent.Add(new StreamContent(new MemoryStream(Encoding.ASCII.GetBytes("Here's a string."))));
+
+ // Act
+ var httpResponseMessage = await _client.PostAsync("http://localhost:" + server.Ports[0] + "/__admin/files/filename.txt", multipartFormDataContent);
+
+ // Assert
+ Check.That(httpResponseMessage.StatusCode).Equals(HttpStatusCode.OK);
+
+ // Verify
+ filesystemHandlerMock.Verify(fs => fs.GetMappingFolder(), Times.Once);
+ filesystemHandlerMock.Verify(fs => fs.FolderExists(It.IsAny()), Times.Once);
+ filesystemHandlerMock.Verify(fs => fs.CreateFolder(It.Is(p => p == "x")), Times.Once);
+ filesystemHandlerMock.Verify(fs => fs.WriteFile(It.Is(p => p == "filename.txt"), It.IsAny()), Times.Once);
+ filesystemHandlerMock.VerifyNoOtherCalls();
+ }
+
+ [Fact]
+ public async Task FluentMockServer_Admin_Files_GetAscii()
+ {
+ // Arrange
+ var filesystemHandlerMock = new Mock(MockBehavior.Strict);
+ filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny())).Returns(true);
+ filesystemHandlerMock.Setup(fs => fs.ReadFile(It.IsAny())).Returns(Encoding.ASCII.GetBytes("Here's a string."));
+
+ var server = FluentMockServer.Start(new FluentMockServerSettings
+ {
+ UseSSL = false,
+ StartAdminInterface = true,
+ FileSystemHandler = filesystemHandlerMock.Object
+ });
+
+ var multipartFormDataContent = new MultipartFormDataContent();
+ multipartFormDataContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
+ multipartFormDataContent.Add(new StreamContent(new MemoryStream()));
+
+ // Act
+ var httpResponseMessageGet = await _client.GetAsync("http://localhost:" + server.Ports[0] + "/__admin/files/filename.txt");
+
+ // Assert
+ Check.That(httpResponseMessageGet.StatusCode).Equals(HttpStatusCode.OK);
+
+ Check.That(httpResponseMessageGet.Content.ReadAsStringAsync().Result).Equals("Here's a string.");
+
+ Check.That(server.LogEntries.Count().Equals(2));
+
+ // Verify
+ filesystemHandlerMock.Verify(fs => fs.ReadFile(It.Is(p => p == "filename.txt")), Times.Once);
+ filesystemHandlerMock.Verify(fs => fs.FileExists(It.Is(p => p == "filename.txt")), Times.Once);
+ filesystemHandlerMock.VerifyNoOtherCalls();
+ }
+
+ [Fact]
+ public async Task FluentMockServer_Admin_Files_GetUTF16()
+ {
+ // Arrange
+ byte[] symbol = Encoding.UTF32.GetBytes(char.ConvertFromUtf32(0x1D161));
+ var filesystemHandlerMock = new Mock(MockBehavior.Strict);
+ filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny())).Returns(true);
+ filesystemHandlerMock.Setup(fs => fs.ReadFile(It.IsAny())).Returns(symbol);
+
+ var server = FluentMockServer.Start(new FluentMockServerSettings
+ {
+ UseSSL = false,
+ StartAdminInterface = true,
+ FileSystemHandler = filesystemHandlerMock.Object
+ });
+
+ var multipartFormDataContent = new MultipartFormDataContent();
+ multipartFormDataContent.Headers.ContentType = MediaTypeHeaderValue.Parse("multipart/form-data");
+ multipartFormDataContent.Add(new StreamContent(new MemoryStream()));
+
+ // Act
+ var httpResponseMessageGet = await _client.GetAsync("http://localhost:" + server.Ports[0] + "/__admin/files/filename.bin");
+
+ // Assert
+ Check.That(httpResponseMessageGet.StatusCode).Equals(HttpStatusCode.OK);
+ Check.That(httpResponseMessageGet.Content.ReadAsByteArrayAsync().Result).Equals(symbol);
+ Check.That(server.LogEntries.Count().Equals(2));
+
+ // Verify
+ filesystemHandlerMock.Verify(fs => fs.ReadFile(It.Is(p => p == "filename.bin")), Times.Once);
+ filesystemHandlerMock.Verify(fs => fs.FileExists(It.Is(p => p == "filename.bin")), Times.Once);
+ filesystemHandlerMock.VerifyNoOtherCalls();
+ }
+
+ [Fact]
+ public async Task FluentMockServer_Admin_Files_Head()
+ {
+ // Arrange
+ var filesystemHandlerMock = new Mock(MockBehavior.Strict);
+ filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny())).Returns(true);
+
+ var server = FluentMockServer.Start(new FluentMockServerSettings
+ {
+ UseSSL = false,
+ StartAdminInterface = true,
+ FileSystemHandler = filesystemHandlerMock.Object
+ });
+
+ // Act
+ var requestUri = "http://localhost:" + server.Ports[0] + "/__admin/files/filename.txt";
+ var httpRequestMessage = new HttpRequestMessage(HttpMethod.Head, requestUri);
+ var httpResponseMessage = await _client.SendAsync(httpRequestMessage);
+
+ // Assert
+ Check.That(httpResponseMessage.StatusCode).Equals(HttpStatusCode.NoContent);
+ Check.That(server.LogEntries.Count().Equals(1));
+
+ // Verify
+ filesystemHandlerMock.Verify(fs => fs.FileExists(It.IsAny()), Times.Once);
+ filesystemHandlerMock.VerifyNoOtherCalls();
+ }
+
+ [Fact]
+ public async Task FluentMockServer_Admin_Files_Head_FileDoesNotExistsReturns404()
+ {
+ // Arrange
+ var filesystemHandlerMock = new Mock(MockBehavior.Strict);
+ filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny())).Returns(false);
+
+ var server = FluentMockServer.Start(new FluentMockServerSettings
+ {
+ UseSSL = false,
+ StartAdminInterface = true,
+ FileSystemHandler = filesystemHandlerMock.Object
+ });
+
+ // Act
+ var requestUri = "http://localhost:" + server.Ports[0] + "/__admin/files/filename.txt";
+ var httpRequestMessage = new HttpRequestMessage(HttpMethod.Head, requestUri);
+ var httpResponseMessage = await _client.SendAsync(httpRequestMessage);
+
+ // Assert
+ Check.That(httpResponseMessage.StatusCode).Equals(HttpStatusCode.NotFound);
+ Check.That(server.LogEntries.Count().Equals(1));
+
+ // Verify
+ filesystemHandlerMock.Verify(fs => fs.FileExists(It.IsAny()), Times.Once);
+ filesystemHandlerMock.VerifyNoOtherCalls();
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/FluentMockServerAdminRestClientTests.cs b/test/WireMock.Net.Tests/FluentMockServerAdminRestClientTests.cs
index cfaac4d9..cbfcf271 100644
--- a/test/WireMock.Net.Tests/FluentMockServerAdminRestClientTests.cs
+++ b/test/WireMock.Net.Tests/FluentMockServerAdminRestClientTests.cs
@@ -1,12 +1,15 @@
-using NFluent;
+using Moq;
+using NFluent;
using RestEase;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
+using System.Text;
using System.Threading.Tasks;
using WireMock.Admin.Mappings;
using WireMock.Admin.Settings;
using WireMock.Client;
+using WireMock.Handlers;
using WireMock.Logging;
using WireMock.Server;
using WireMock.Settings;
@@ -232,6 +235,237 @@ namespace WireMock.Net.Tests
Check.That(requestLogged.Request.Body).IsNotNull();
Check.That(requestLogged.Request.Body).Contains("T000001");
}
- }
+ [Fact]
+ public async Task IFluentMockServerAdmin_PostFileAsync_Ascii()
+ {
+ // Arrange
+ var filesystemHandlerMock = new Mock(MockBehavior.Strict);
+ filesystemHandlerMock.Setup(fs => fs.GetMappingFolder()).Returns("__admin/mappings");
+ filesystemHandlerMock.Setup(fs => fs.FolderExists(It.IsAny())).Returns(true);
+ filesystemHandlerMock.Setup(fs => fs.WriteFile(It.IsAny(), It.IsAny()));
+
+ var server = FluentMockServer.Start(new FluentMockServerSettings
+ {
+ UseSSL = false,
+ StartAdminInterface = true,
+ FileSystemHandler = filesystemHandlerMock.Object
+ });
+
+ var api = RestClient.For(server.Urls[0]);
+
+ // Act
+ var request = await api.PostFileAsync("filename.txt", "abc");
+
+ // Assert
+ Check.That(request.Guid).IsNull();
+ Check.That(request.Status).Contains("File");
+
+ // Verify
+ filesystemHandlerMock.Verify(fs => fs.GetMappingFolder(), Times.Once);
+ filesystemHandlerMock.Verify(fs => fs.FolderExists(It.IsAny()), Times.Once);
+ filesystemHandlerMock.Verify(fs => fs.WriteFile(It.Is(p => p == "filename.txt"), It.IsAny()), Times.Once);
+ filesystemHandlerMock.VerifyNoOtherCalls();
+
+ server.Stop();
+ }
+
+ [Fact]
+ public async Task IFluentMockServerAdmin_PutFileAsync_Ascii()
+ {
+ // Arrange
+ var filesystemHandlerMock = new Mock(MockBehavior.Strict);
+ filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny())).Returns(true);
+ filesystemHandlerMock.Setup(fs => fs.WriteFile(It.IsAny(), It.IsAny()));
+
+ var server = FluentMockServer.Start(new FluentMockServerSettings
+ {
+ UseSSL = false,
+ StartAdminInterface = true,
+ FileSystemHandler = filesystemHandlerMock.Object
+ });
+
+ var api = RestClient.For(server.Urls[0]);
+
+ // Act
+ var request = await api.PutFileAsync("filename.txt", "abc-abc");
+
+ // Assert
+ Check.That(request.Guid).IsNull();
+ Check.That(request.Status).Contains("File");
+
+ // Verify
+ filesystemHandlerMock.Verify(fs => fs.WriteFile(It.Is(p => p == "filename.txt"), It.IsAny()), Times.Once);
+ filesystemHandlerMock.Verify(fs => fs.FileExists(It.Is(p => p == "filename.txt")), Times.Once);
+ filesystemHandlerMock.VerifyNoOtherCalls();
+
+ server.Stop();
+ }
+
+ [Fact]
+ public void IFluentMockServerAdmin_PutFileAsync_NotFound()
+ {
+ // Arrange
+ var filesystemHandlerMock = new Mock(MockBehavior.Strict);
+ filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny())).Returns(false);
+
+ var server = FluentMockServer.Start(new FluentMockServerSettings
+ {
+ UseSSL = false,
+ StartAdminInterface = true,
+ FileSystemHandler = filesystemHandlerMock.Object
+ });
+
+ var api = RestClient.For(server.Urls[0]);
+
+ // Act and Assert
+ Check.ThatAsyncCode(() => api.PutFileAsync("filename.txt", "xxx")).Throws();
+
+ // Verify
+ filesystemHandlerMock.Verify(fs => fs.FileExists(It.Is(p => p == "filename.txt")), Times.Once);
+ filesystemHandlerMock.VerifyNoOtherCalls();
+
+ server.Stop();
+ }
+
+ [Fact]
+ public void IFluentMockServerAdmin_GetFileAsync_NotFound()
+ {
+ // Arrange
+ var filesystemHandlerMock = new Mock(MockBehavior.Strict);
+ filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny())).Returns(false);
+ filesystemHandlerMock.Setup(fs => fs.ReadFile(It.IsAny())).Returns(Encoding.ASCII.GetBytes("Here's a string."));
+
+ var server = FluentMockServer.Start(new FluentMockServerSettings
+ {
+ UseSSL = false,
+ StartAdminInterface = true,
+ FileSystemHandler = filesystemHandlerMock.Object
+ });
+
+ var api = RestClient.For(server.Urls[0]);
+
+ // Act and Assert
+ Check.ThatAsyncCode(() => api.GetFileAsync("filename.txt")).Throws();
+
+ // Verify
+ filesystemHandlerMock.Verify(fs => fs.FileExists(It.Is(p => p == "filename.txt")), Times.Once);
+ filesystemHandlerMock.VerifyNoOtherCalls();
+
+ server.Stop();
+ }
+
+ [Fact]
+ public async Task IFluentMockServerAdmin_GetFileAsync_Found()
+ {
+ // Arrange
+ string data = "Here's a string.";
+ var filesystemHandlerMock = new Mock(MockBehavior.Strict);
+ filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny())).Returns(true);
+ filesystemHandlerMock.Setup(fs => fs.ReadFile(It.IsAny())).Returns(Encoding.ASCII.GetBytes(data));
+
+ var server = FluentMockServer.Start(new FluentMockServerSettings
+ {
+ UseSSL = false,
+ StartAdminInterface = true,
+ FileSystemHandler = filesystemHandlerMock.Object
+ });
+
+ var api = RestClient.For(server.Urls[0]);
+
+ // Act
+ string file = await api.GetFileAsync("filename.txt");
+
+ // Assert
+ Check.That(file).Equals(data);
+
+ // Verify
+ filesystemHandlerMock.Verify(fs => fs.FileExists(It.Is(p => p == "filename.txt")), Times.Once);
+ filesystemHandlerMock.Verify(fs => fs.ReadFile(It.Is(p => p == "filename.txt")), Times.Once);
+ filesystemHandlerMock.VerifyNoOtherCalls();
+
+ server.Stop();
+ }
+
+ [Fact]
+ public async Task IFluentMockServerAdmin_DeleteFileAsync_Ok()
+ {
+ // Arrange
+ var filesystemHandlerMock = new Mock(MockBehavior.Strict);
+ filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny())).Returns(true);
+ filesystemHandlerMock.Setup(fs => fs.DeleteFile(It.IsAny()));
+
+ var server = FluentMockServer.Start(new FluentMockServerSettings
+ {
+ UseSSL = false,
+ StartAdminInterface = true,
+ FileSystemHandler = filesystemHandlerMock.Object
+ });
+
+ var api = RestClient.For(server.Urls[0]);
+
+ // Act
+ await api.DeleteFileAsync("filename.txt");
+
+ // Verify
+ filesystemHandlerMock.Verify(fs => fs.FileExists(It.Is(p => p == "filename.txt")), Times.Once);
+ filesystemHandlerMock.Verify(fs => fs.DeleteFile(It.Is(p => p == "filename.txt")), Times.Once);
+ filesystemHandlerMock.VerifyNoOtherCalls();
+
+ server.Stop();
+ }
+
+ [Fact]
+ public void IFluentMockServerAdmin_DeleteFileAsync_NotFound()
+ {
+ // Arrange
+ var filesystemHandlerMock = new Mock(MockBehavior.Strict);
+ filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny())).Returns(false);
+ filesystemHandlerMock.Setup(fs => fs.DeleteFile(It.IsAny()));
+
+ var server = FluentMockServer.Start(new FluentMockServerSettings
+ {
+ UseSSL = false,
+ StartAdminInterface = true,
+ FileSystemHandler = filesystemHandlerMock.Object
+ });
+
+ var api = RestClient.For(server.Urls[0]);
+
+ // Act and Assert
+ Check.ThatAsyncCode(() => api.DeleteFileAsync("filename.txt")).Throws();
+
+ // Verify
+ filesystemHandlerMock.Verify(fs => fs.FileExists(It.Is(p => p == "filename.txt")), Times.Once);
+ filesystemHandlerMock.VerifyNoOtherCalls();
+
+ server.Stop();
+ }
+
+ [Fact]
+ public void IFluentMockServerAdmin_FileExistsAsync_NotFound()
+ {
+ // Arrange
+ var filesystemHandlerMock = new Mock(MockBehavior.Strict);
+ filesystemHandlerMock.Setup(fs => fs.FileExists(It.IsAny())).Returns(false);
+
+ var server = FluentMockServer.Start(new FluentMockServerSettings
+ {
+ UseSSL = false,
+ StartAdminInterface = true,
+ FileSystemHandler = filesystemHandlerMock.Object
+ });
+
+ var api = RestClient.For(server.Urls[0]);
+
+ // Act and Assert
+ Check.ThatAsyncCode(() => api.FileExistsAsync("filename.txt")).Throws();
+
+ // Verify
+ filesystemHandlerMock.Verify(fs => fs.FileExists(It.Is(p => p == "filename.txt")), Times.Once);
+ filesystemHandlerMock.VerifyNoOtherCalls();
+
+ server.Stop();
+ }
+ }
}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/FluentMockServerTests.Admin.cs b/test/WireMock.Net.Tests/FluentMockServerTests.Admin.cs
index 17baa808..471d1b85 100644
--- a/test/WireMock.Net.Tests/FluentMockServerTests.Admin.cs
+++ b/test/WireMock.Net.Tests/FluentMockServerTests.Admin.cs
@@ -1,11 +1,11 @@
-using System;
+using Moq;
+using Newtonsoft.Json;
+using NFluent;
+using System;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Threading.Tasks;
-using Moq;
-using Newtonsoft.Json;
-using NFluent;
using WireMock.Handlers;
using WireMock.Logging;
using WireMock.RequestBuilders;
diff --git a/test/WireMock.Net.Tests/FluentMockServerTests.Settings.cs b/test/WireMock.Net.Tests/FluentMockServerTests.Settings.cs
index 920a5424..b819a397 100644
--- a/test/WireMock.Net.Tests/FluentMockServerTests.Settings.cs
+++ b/test/WireMock.Net.Tests/FluentMockServerTests.Settings.cs
@@ -62,7 +62,7 @@ namespace WireMock.Net.Tests
// Assert
var mappings = server.Mappings;
- Check.That(mappings.Count()).IsEqualTo(19);
+ Check.That(mappings.Count()).IsEqualTo(24);
Check.That(mappings.All(m => m.Priority == int.MinValue)).IsTrue();
}
@@ -81,8 +81,8 @@ namespace WireMock.Net.Tests
// Assert
var mappings = server.Mappings;
- Check.That(mappings.Count()).IsEqualTo(20);
- Check.That(mappings.Count(m => m.Priority == int.MinValue)).IsEqualTo(19);
+ Check.That(mappings.Count()).IsEqualTo(25);
+ Check.That(mappings.Count(m => m.Priority == int.MinValue)).IsEqualTo(24);
Check.That(mappings.Count(m => m.Priority == 1000)).IsEqualTo(1);
}
diff --git a/test/WireMock.Net.Tests/Handlers/LocalFileSystemHandlerTests.cs b/test/WireMock.Net.Tests/Handlers/LocalFileSystemHandlerTests.cs
index 24c42956..b8d06c95 100644
--- a/test/WireMock.Net.Tests/Handlers/LocalFileSystemHandlerTests.cs
+++ b/test/WireMock.Net.Tests/Handlers/LocalFileSystemHandlerTests.cs
@@ -1,6 +1,6 @@
-using System;
+using NFluent;
+using System;
using System.IO;
-using NFluent;
using WireMock.Handlers;
using Xunit;
@@ -21,24 +21,62 @@ namespace WireMock.Net.Tests.Handlers
}
[Fact]
- public void LocalFileSystemHandler_CreateFolder_Throws()
+ public void LocalFileSystemHandler_CreateFolder_ThrowsArgumentNullException()
{
// Act
Check.ThatCode(() => _sut.CreateFolder(null)).Throws();
}
[Fact]
- public void LocalFileSystemHandler_WriteMappingFile_Throws()
+ public void LocalFileSystemHandler_WriteMappingFile_ThrowsArgumentNullException()
{
// Act
Check.ThatCode(() => _sut.WriteMappingFile(null, null)).Throws();
}
[Fact]
- public void LocalFileSystemHandler_ReadResponseBodyAsFile_Throws()
+ public void LocalFileSystemHandler_ReadResponseBodyAsFile_ThrowsArgumentNullException()
{
// Act
Check.ThatCode(() => _sut.ReadResponseBodyAsFile(null)).Throws();
}
+
+ [Fact]
+ public void LocalFileSystemHandler_FileExists_ReturnsFalse()
+ {
+ // Act
+ var result = _sut.FileExists("x.x");
+
+ // Assert
+ Check.That(result).IsFalse();
+ }
+
+ [Fact]
+ public void LocalFileSystemHandler_FileExists_ThrowsArgumentNullException()
+ {
+ // Act
+ Check.ThatCode(() => _sut.FileExists(null)).Throws();
+ }
+
+ [Fact]
+ public void LocalFileSystemHandler_ReadFile_ThrowsArgumentNullException()
+ {
+ // Act
+ Check.ThatCode(() => _sut.ReadFile(null)).Throws();
+ }
+
+ [Fact]
+ public void LocalFileSystemHandler_WriteFile_ThrowsArgumentNullException()
+ {
+ // Act
+ Check.ThatCode(() => _sut.WriteFile(null, null)).Throws();
+ }
+
+ [Fact]
+ public void LocalFileSystemHandler_DeleteFile_ThrowsArgumentNullException()
+ {
+ // Act
+ Check.ThatCode(() => _sut.DeleteFile(null)).Throws();
+ }
}
}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/Matchers/JmesPathMatcherTests.cs b/test/WireMock.Net.Tests/Matchers/JmesPathMatcherTests.cs
new file mode 100644
index 00000000..bef4724f
--- /dev/null
+++ b/test/WireMock.Net.Tests/Matchers/JmesPathMatcherTests.cs
@@ -0,0 +1,166 @@
+using Newtonsoft.Json.Linq;
+using NFluent;
+using WireMock.Matchers;
+using Xunit;
+
+namespace WireMock.Net.Tests.Matchers
+{
+ public class JmesPathMatcherTests
+ {
+ [Fact]
+ public void JmesPathMatcher_GetName()
+ {
+ // Assign
+ var matcher = new JmesPathMatcher("X");
+
+ // Act
+ string name = matcher.Name;
+
+ // Assert
+ Check.That(name).Equals("JmesPathMatcher");
+ }
+
+ [Fact]
+ public void JmesPathMatcher_GetPatterns()
+ {
+ // Assign
+ var matcher = new JmesPathMatcher("X");
+
+ // Act
+ string[] patterns = matcher.GetPatterns();
+
+ // Assert
+ Check.That(patterns).ContainsExactly("X");
+ }
+
+ [Fact]
+ public void JmesPathMatcher_IsMatch_ByteArray()
+ {
+ // Assign
+ var bytes = new byte[0];
+ var matcher = new JmesPathMatcher("");
+
+ // Act
+ double match = matcher.IsMatch(bytes);
+
+ // Assert
+ Check.That(match).IsEqualTo(0);
+ }
+
+ [Fact]
+ public void JmesPathMatcher_IsMatch_NullString()
+ {
+ // Assign
+ string s = null;
+ var matcher = new JmesPathMatcher("");
+
+ // Act
+ double match = matcher.IsMatch(s);
+
+ // Assert
+ Check.That(match).IsEqualTo(0);
+ }
+
+ [Fact]
+ public void JmesPathMatcher_IsMatch_NullObject()
+ {
+ // Assign
+ object o = null;
+ var matcher = new JmesPathMatcher("");
+
+ // Act
+ double match = matcher.IsMatch(o);
+
+ // Assert
+ Check.That(match).IsEqualTo(0);
+ }
+
+ [Fact]
+ public void JmesPathMatcher_IsMatch_String_Exception_Mismatch()
+ {
+ // Assign
+ var matcher = new JmesPathMatcher("xxx");
+
+ // Act
+ double match = matcher.IsMatch("");
+
+ // Assert
+ Check.That(match).IsEqualTo(0);
+ }
+
+ [Fact]
+ public void JmesPathMatcher_IsMatch_Object_Exception_Mismatch()
+ {
+ // Assign
+ var matcher = new JmesPathMatcher("");
+
+ // Act
+ double match = matcher.IsMatch("x");
+
+ // Assert
+ Check.That(match).IsEqualTo(0);
+ }
+
+ [Fact]
+ public void JmesPathMatcher_IsMatch_AnonymousObject()
+ {
+ // Assign
+ var matcher = new JmesPathMatcher("things.name == 'RequiredThing'");
+
+ // Act
+ double match = matcher.IsMatch(new { things = new { name = "RequiredThing" } });
+
+ // Assert
+ Check.That(match).IsEqualTo(1);
+ }
+
+ [Fact]
+ public void JmesPathMatcher_IsMatch_JObject()
+ {
+ // Assign
+ string[] patterns = { "things.x == 'RequiredThing'" };
+ var matcher = new JmesPathMatcher(patterns);
+
+ // Act
+ var sub = new JObject
+ {
+ { "x", new JValue("RequiredThing") }
+ };
+ var jobject = new JObject
+ {
+ { "Id", new JValue(1) },
+ { "things", sub }
+ };
+ double match = matcher.IsMatch(jobject);
+
+ // Assert
+ Check.That(match).IsEqualTo(1);
+ }
+
+ [Fact]
+ public void JmesPathMatcher_IsMatch_JObject_Parsed()
+ {
+ // Assign
+ var matcher = new JmesPathMatcher("things.x == 'RequiredThing'");
+
+ // Act
+ double match = matcher.IsMatch(JObject.Parse("{ \"things\": { \"x\": \"RequiredThing\" } }"));
+
+ // Assert
+ Check.That(match).IsEqualTo(1);
+ }
+
+ [Fact]
+ public void JmesPathMatcher_IsMatch_RejectOnMatch()
+ {
+ // Assign
+ var matcher = new JmesPathMatcher(MatchBehaviour.RejectOnMatch, "things.x == 'RequiredThing'");
+
+ // Act
+ double match = matcher.IsMatch(JObject.Parse("{ \"things\": { \"x\": \"RequiredThing\" } }"));
+
+ // Assert
+ Check.That(match).IsEqualTo(0.0);
+ }
+ }
+}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyTests.cs b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyTests.cs
index 54287d2c..4328fd4c 100644
--- a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyTests.cs
+++ b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithBodyTests.cs
@@ -1,4 +1,5 @@
-using System.Text;
+using System;
+using System.Text;
using System.Threading.Tasks;
using NFluent;
using WireMock.Models;
@@ -231,5 +232,53 @@ namespace WireMock.Net.Tests.ResponseBuilders
Check.That(response2Message.BodyData.BodyAsString).IsNull();
Check.That(response2Message.StatusCode).IsEqualTo(200);
}
+
+ [Fact]
+ public async Task Response_ProvideResponse_WithBodyAsFile()
+ {
+ var fileContents = "testFileContents" + Guid.NewGuid();
+ var bodyDataAsFile = new BodyData {BodyAsFile = fileContents};
+
+ var request1 = new RequestMessage(new UrlDetails("http://localhost/__admin/files/filename.txt"), "PUT", ClientIp, bodyDataAsFile);
+
+ var response = Response.Create().WithStatusCode(200).WithBody(fileContents);
+
+ var provideResponseAsync = await response.ProvideResponseAsync(request1);
+
+ Check.That(provideResponseAsync.StatusCode).IsEqualTo(200);
+ Check.That(provideResponseAsync.BodyData.BodyAsString).Contains(fileContents);
+ }
+
+ [Fact]
+ public async Task Response_ProvideResponse_WithResponseAsFile()
+ {
+ var fileContents = "testFileContents" + Guid.NewGuid();
+ var bodyDataAsFile = new BodyData { BodyAsFile = fileContents };
+
+ var request1 = new RequestMessage(new UrlDetails("http://localhost/__admin/files/filename.txt"), "GET", ClientIp, bodyDataAsFile);
+
+ var response = Response.Create().WithStatusCode(200).WithBody(fileContents);
+
+ var provideResponseAsync = await response.ProvideResponseAsync(request1);
+
+ Check.That(provideResponseAsync.StatusCode).IsEqualTo(200);
+ Check.That(provideResponseAsync.BodyData.BodyAsString).Contains(fileContents);
+ }
+
+ [Fact]
+ public async Task Response_ProvideResponse_WithResponseDeleted()
+ {
+ var fileContents = "testFileContents" + Guid.NewGuid();
+ var bodyDataAsFile = new BodyData { BodyAsFile = fileContents };
+
+ var request1 = new RequestMessage(new UrlDetails("http://localhost/__admin/files/filename.txt"), "DELETE", ClientIp, bodyDataAsFile);
+
+ var response = Response.Create().WithStatusCode(200).WithBody("File deleted.");
+
+ var provideResponseAsync = await response.ProvideResponseAsync(request1);
+
+ Check.That(provideResponseAsync.StatusCode).IsEqualTo(200);
+ Check.That(provideResponseAsync.BodyData.BodyAsString).Contains("File deleted.");
+ }
}
}
diff --git a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithHandlebarsTests.cs b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithHandlebarsTests.cs
index d609be14..fad6d28d 100644
--- a/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithHandlebarsTests.cs
+++ b/test/WireMock.Net.Tests/ResponseBuilders/ResponseWithHandlebarsTests.cs
@@ -214,5 +214,49 @@ namespace WireMock.Net.Tests.ResponseBuilders
// Assert
Check.That(JsonConvert.SerializeObject(responseMessage.BodyData.BodyAsJson)).Equals("[\"first\",\"/foo_array\",\"test 1\",\"test 2\",\"last\"]");
}
+
+ [Fact]
+ public async Task Response_ProvideResponse_Handlebars_WithBodyAsFile()
+ {
+ // Assign
+ var request = new RequestMessage(new UrlDetails("http://localhost/foo?MyUniqueNumber=1"), "GET", ClientIp);
+
+ var response = Response.Create()
+ .WithTransformer()
+ .WithBodyFromFile(@"c:\\{{request.query.MyUniqueNumber}}\test.xml"); // why use a \\ here ?
+
+ // Act
+ var responseMessage = await response.ProvideResponseAsync(request);
+
+ // Assert
+ Check.That(responseMessage.BodyData.BodyAsFile).Equals(@"c:\1\test.xml");
+ }
+
+ [Fact]
+ public async Task Response_ProvideResponse_Handlebars_WithBodyAsFile_JsonPath()
+ {
+ // Assign
+ string jsonString = "{ \"MyUniqueNumber\": \"1\" }";
+ var bodyData = new BodyData
+ {
+ BodyAsString = jsonString,
+ BodyAsJson = JsonConvert.DeserializeObject(jsonString),
+ DetectedBodyType = BodyType.Json,
+ DetectedBodyTypeFromContentType = BodyType.Json,
+ Encoding = Encoding.UTF8
+ };
+ var request = new RequestMessage(new UrlDetails("http://localhost/foo"), "POST", ClientIp, bodyData);
+
+ string jsonPath = "\"$.MyUniqueNumber\"";
+ var response = Response.Create()
+ .WithTransformer()
+ .WithBodyFromFile(@"c:\\{{JsonPath.SelectToken request.body " + jsonPath + "}}\\test.json"); // why use a \\ here ?
+
+ // Act
+ var responseMessage = await response.ProvideResponseAsync(request);
+
+ // Assert
+ Check.That(responseMessage.BodyData.BodyAsFile).Equals(@"c:\1\test.json");
+ }
}
}
\ No newline at end of file
diff --git a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj
index d8e5e9ea..16306f97 100644
--- a/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj
+++ b/test/WireMock.Net.Tests/WireMock.Net.Tests.csproj
@@ -36,7 +36,7 @@
-
+
all