diff --git a/src/Serval.Client/Client.g.cs b/src/Serval.Client/Client.g.cs index e44dbf1b..02de8cdf 100644 --- a/src/Serval.Client/Client.g.cs +++ b/src/Serval.Client/Client.g.cs @@ -970,26 +970,15 @@ public partial interface ITranslationEnginesClient ///
* The references defined in the SourceFile per line, if any. ///
* An auto-generated reference of `[TextId]:[lineNumber]`, 1 indexed. ///
* **Translation**: the text of the pretranslation + ///
+ ///
Pretranslations can be filtered by text id if provided. /// /// The translation engine id /// The corpus id + /// The text id (optional) /// The pretranslations /// A server side error occurred. - System.Threading.Tasks.Task> GetAllPretranslationsAsync(string id, string corpusId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Gets all pretranslations from a TextId - /// - /// - /// Similar to "get all pretranslations of a corpus," except that the results are filtered by TextId. - /// - /// The translation engine id - /// The corpus id - /// The text id - /// The pretranslations - /// A server side error occurred. - System.Threading.Tasks.Task> GetAllPretranslations2Async(string id, string corpusId, string textId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); + System.Threading.Tasks.Task> GetAllPretranslationsAsync(string id, string corpusId, string? textId = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)); /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. /// @@ -2588,12 +2577,15 @@ public string BaseUrl ///
* The references defined in the SourceFile per line, if any. ///
* An auto-generated reference of `[TextId]:[lineNumber]`, 1 indexed. ///
* **Translation**: the text of the pretranslation + ///
+ ///
Pretranslations can be filtered by text id if provided. /// /// The translation engine id /// The corpus id + /// The text id (optional) /// The pretranslations /// A server side error occurred. - public virtual async System.Threading.Tasks.Task> GetAllPretranslationsAsync(string id, string corpusId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) + public virtual async System.Threading.Tasks.Task> GetAllPretranslationsAsync(string id, string corpusId, string? textId = null, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { if (id == null) throw new System.ArgumentNullException("id"); @@ -2602,133 +2594,14 @@ public string BaseUrl throw new System.ArgumentNullException("corpusId"); var urlBuilder_ = new System.Text.StringBuilder(); - urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/translation/engines/{id}/corpora/{corpusId}/pretranslations"); + urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/translation/engines/{id}/corpora/{corpusId}/pretranslations?"); urlBuilder_.Replace("{id}", System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); urlBuilder_.Replace("{corpusId}", System.Uri.EscapeDataString(ConvertToString(corpusId, System.Globalization.CultureInfo.InvariantCulture))); - - var client_ = _httpClient; - var disposeClient_ = false; - try - { - using (var request_ = new System.Net.Http.HttpRequestMessage()) - { - request_.Method = new System.Net.Http.HttpMethod("GET"); - request_.Headers.Accept.Add(System.Net.Http.Headers.MediaTypeWithQualityHeaderValue.Parse("application/json")); - - PrepareRequest(client_, request_, urlBuilder_); - - var url_ = urlBuilder_.ToString(); - request_.RequestUri = new System.Uri(url_, System.UriKind.RelativeOrAbsolute); - - PrepareRequest(client_, request_, url_); - - var response_ = await client_.SendAsync(request_, System.Net.Http.HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); - var disposeResponse_ = true; - try - { - var headers_ = System.Linq.Enumerable.ToDictionary(response_.Headers, h_ => h_.Key, h_ => h_.Value); - if (response_.Content != null && response_.Content.Headers != null) - { - foreach (var item_ in response_.Content.Headers) - headers_[item_.Key] = item_.Value; - } - - ProcessResponse(client_, response_); - - var status_ = (int)response_.StatusCode; - if (status_ == 200) - { - var objectResponse_ = await ReadObjectResponseAsync>(response_, headers_, cancellationToken).ConfigureAwait(false); - if (objectResponse_.Object == null) - { - throw new ServalApiException("Response was null which was not expected.", status_, objectResponse_.Text, headers_, null); - } - return objectResponse_.Object; - } - else - if (status_ == 401) - { - string responseText_ = ( response_.Content == null ) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ServalApiException("The client is not authenticated", status_, responseText_, headers_, null); - } - else - if (status_ == 403) - { - string responseText_ = ( response_.Content == null ) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ServalApiException("The authenticated client cannot perform the operation or does not own the translation engine", status_, responseText_, headers_, null); - } - else - if (status_ == 404) - { - string responseText_ = ( response_.Content == null ) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ServalApiException("The engine or corpus does not exist", status_, responseText_, headers_, null); - } - else - if (status_ == 405) - { - string responseText_ = ( response_.Content == null ) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ServalApiException("The method is not supported", status_, responseText_, headers_, null); - } - else - if (status_ == 409) - { - string responseText_ = ( response_.Content == null ) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ServalApiException("The engine needs to be built first", status_, responseText_, headers_, null); - } - else - if (status_ == 503) - { - string responseText_ = ( response_.Content == null ) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ServalApiException("A necessary service is currently unavailable. Check `/health` for more details. ", status_, responseText_, headers_, null); - } - else - { - var responseData_ = response_.Content == null ? null : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ServalApiException("The HTTP status code of the response was not expected (" + status_ + ").", status_, responseData_, headers_, null); - } - } - finally - { - if (disposeResponse_) - response_.Dispose(); - } - } - } - finally + if (textId != null) { - if (disposeClient_) - client_.Dispose(); + urlBuilder_.Append(System.Uri.EscapeDataString("textId") + "=").Append(System.Uri.EscapeDataString(ConvertToString(textId, System.Globalization.CultureInfo.InvariantCulture))).Append("&"); } - } - - /// A cancellation token that can be used by other objects or threads to receive notice of cancellation. - /// - /// Gets all pretranslations from a TextId - /// - /// - /// Similar to "get all pretranslations of a corpus," except that the results are filtered by TextId. - /// - /// The translation engine id - /// The corpus id - /// The text id - /// The pretranslations - /// A server side error occurred. - public virtual async System.Threading.Tasks.Task> GetAllPretranslations2Async(string id, string corpusId, string textId, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) - { - if (id == null) - throw new System.ArgumentNullException("id"); - - if (corpusId == null) - throw new System.ArgumentNullException("corpusId"); - - if (textId == null) - throw new System.ArgumentNullException("textId"); - - var urlBuilder_ = new System.Text.StringBuilder(); - urlBuilder_.Append(BaseUrl != null ? BaseUrl.TrimEnd('/') : "").Append("/translation/engines/{id}/corpora/{corpusId}/pretranslations/{textId}"); - urlBuilder_.Replace("{id}", System.Uri.EscapeDataString(ConvertToString(id, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Replace("{corpusId}", System.Uri.EscapeDataString(ConvertToString(corpusId, System.Globalization.CultureInfo.InvariantCulture))); - urlBuilder_.Replace("{textId}", System.Uri.EscapeDataString(ConvertToString(textId, System.Globalization.CultureInfo.InvariantCulture))); + urlBuilder_.Length--; var client_ = _httpClient; var disposeClient_ = false; @@ -2785,13 +2658,7 @@ public string BaseUrl if (status_ == 404) { string responseText_ = ( response_.Content == null ) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ServalApiException("The engine or corpus or text does not exist", status_, responseText_, headers_, null); - } - else - if (status_ == 405) - { - string responseText_ = ( response_.Content == null ) ? string.Empty : await response_.Content.ReadAsStringAsync().ConfigureAwait(false); - throw new ServalApiException("The method is not supported", status_, responseText_, headers_, null); + throw new ServalApiException("The engine or corpus does not exist", status_, responseText_, headers_, null); } else if (status_ == 409) diff --git a/src/Serval.Shared/Controllers/AbortedRpcExceptionFilter.cs b/src/Serval.Shared/Controllers/AbortedRpcExceptionFilter.cs new file mode 100644 index 00000000..1685ffd5 --- /dev/null +++ b/src/Serval.Shared/Controllers/AbortedRpcExceptionFilter.cs @@ -0,0 +1,13 @@ +namespace Serval.Shared.Controllers; + +public class AbortedRpcExceptionFilter : ExceptionFilterAttribute +{ + public override void OnException(ExceptionContext context) + { + if (context.Exception is RpcException rpcException && rpcException.StatusCode == StatusCode.Aborted) + { + context.Result = new ConflictObjectResult(rpcException.Message); + context.ExceptionHandled = true; + } + } +} diff --git a/src/Serval.Shared/Controllers/HttpResultFilter.cs b/src/Serval.Shared/Controllers/ErrorResultFilter.cs similarity index 74% rename from src/Serval.Shared/Controllers/HttpResultFilter.cs rename to src/Serval.Shared/Controllers/ErrorResultFilter.cs index eb381ae2..68207df8 100644 --- a/src/Serval.Shared/Controllers/HttpResultFilter.cs +++ b/src/Serval.Shared/Controllers/ErrorResultFilter.cs @@ -2,13 +2,13 @@ namespace Serval.Shared.Controllers { - public class HttpResultFilter : ResultFilterAttribute + public class ErrorResultFilter : ResultFilterAttribute { private readonly ILogger _logger; - public HttpResultFilter(ILoggerFactory loggerFactory) + public ErrorResultFilter(ILoggerFactory loggerFactory) { - _logger = loggerFactory.CreateLogger(); + _logger = loggerFactory.CreateLogger(); } public override Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) diff --git a/src/Serval.Shared/Controllers/ServalControllerBase.cs b/src/Serval.Shared/Controllers/ServalControllerBase.cs index 7ca55c75..bebcdf29 100644 --- a/src/Serval.Shared/Controllers/ServalControllerBase.cs +++ b/src/Serval.Shared/Controllers/ServalControllerBase.cs @@ -4,8 +4,9 @@ [Produces("application/json")] [TypeFilter(typeof(OperationCancelledExceptionFilter))] [TypeFilter(typeof(NotSupportedExceptionFilter))] -[TypeFilter(typeof(ServiceUnavailableException))] -[TypeFilter(typeof(HttpResultFilter))] +[TypeFilter(typeof(ServiceUnavailableExceptionFilter))] +[TypeFilter(typeof(ErrorResultFilter))] +[TypeFilter(typeof(AbortedRpcExceptionFilter))] public abstract class ServalControllerBase : Controller { private readonly IAuthorizationService _authService; diff --git a/src/Serval.Shared/Controllers/ServiceUnavailableExceptionFilter.cs b/src/Serval.Shared/Controllers/ServiceUnavailableExceptionFilter.cs index e9039f08..b7f2ca4d 100644 --- a/src/Serval.Shared/Controllers/ServiceUnavailableExceptionFilter.cs +++ b/src/Serval.Shared/Controllers/ServiceUnavailableExceptionFilter.cs @@ -1,21 +1,19 @@ -using Newtonsoft.Json.Linq; - namespace Serval.Shared.Controllers; -public class ServiceUnavailableException : ExceptionFilterAttribute +public class ServiceUnavailableExceptionFilter : ExceptionFilterAttribute { - private readonly ILogger _logger; + private readonly ILogger _logger; - public ServiceUnavailableException(ILoggerFactory loggerFactory) + public ServiceUnavailableExceptionFilter(ILoggerFactory loggerFactory) { - _logger = loggerFactory.CreateLogger(); + _logger = loggerFactory.CreateLogger(); } public override void OnException(ExceptionContext context) { if ( - (context.Exception is System.TimeoutException) - || (context.Exception is Grpc.Core.RpcException rpcEx && rpcEx.StatusCode == StatusCode.Unavailable) + (context.Exception is TimeoutException) + || (context.Exception is RpcException rpcEx && rpcEx.StatusCode == StatusCode.Unavailable) ) { _logger.Log( diff --git a/src/Serval.Translation/Controllers/TranslationEnginesController.cs b/src/Serval.Translation/Controllers/TranslationEnginesController.cs index fd66331d..8507a721 100644 --- a/src/Serval.Translation/Controllers/TranslationEnginesController.cs +++ b/src/Serval.Translation/Controllers/TranslationEnginesController.cs @@ -1,7 +1,4 @@ -using Serval.Translation.Models; -using System.Threading; - -namespace Serval.Translation.Controllers; +namespace Serval.Translation.Controllers; [ApiVersion(1.0)] [Route("api/v{version:apiVersion}/translation/engines")] @@ -218,8 +215,6 @@ CancellationToken cancellationToken { if (!(await AuthorizeAsync(id, cancellationToken)).IsSuccess(out ActionResult? errorResult)) return errorResult; - if (!await IsBuilt(id)) - return Conflict(); TranslationResult? result = await _engineService.TranslateAsync(id, segment, cancellationToken); if (result == null) @@ -261,8 +256,6 @@ CancellationToken cancellationToken { if (!(await AuthorizeAsync(id, cancellationToken)).IsSuccess(out ActionResult? errorResult)) return errorResult; - if (!await IsBuilt(id)) - return Conflict(); IEnumerable? results = await _engineService.TranslateAsync( id, @@ -307,8 +300,6 @@ CancellationToken cancellationToken { if (!(await AuthorizeAsync(id, cancellationToken)).IsSuccess(out ActionResult? errorResult)) return errorResult; - if (!await IsBuilt(id)) - return Conflict(); WordGraph? wordGraph = await _engineService.GetWordGraphAsync(id, segment, cancellationToken); if (wordGraph == null) @@ -351,8 +342,6 @@ CancellationToken cancellationToken { if (!(await AuthorizeAsync(id, cancellationToken)).IsSuccess(out ActionResult? errorResult)) return errorResult; - if (!await IsBuilt(id)) - return Conflict(); if ( !await _engineService.TrainSegmentPairAsync( @@ -600,15 +589,17 @@ CancellationToken cancellationToken /// * The references defined in the SourceFile per line, if any. /// * An auto-generated reference of `[TextId]:[lineNumber]`, 1 indexed. /// * **Translation**: the text of the pretranslation + /// + /// Pretranslations can be filtered by text id if provided. /// /// The translation engine id /// The corpus id + /// The text id (optional) /// /// The pretranslations /// The client is not authenticated /// The authenticated client cannot perform the operation or does not own the translation engine /// The engine or corpus does not exist - /// The method is not supported /// The engine needs to be built first /// A necessary service is currently unavailable. Check `/health` for more details. [Authorize(Scopes.ReadTranslationEngines)] @@ -617,12 +608,12 @@ CancellationToken cancellationToken [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] [ProducesResponseType(typeof(void), StatusCodes.Status404NotFound)] - [ProducesResponseType(typeof(void), StatusCodes.Status405MethodNotAllowed)] [ProducesResponseType(typeof(void), StatusCodes.Status409Conflict)] [ProducesResponseType(typeof(void), StatusCodes.Status503ServiceUnavailable)] public async Task>> GetAllPretranslationsAsync( [NotNull] string id, [NotNull] string corpusId, + [FromQuery] string? textId, CancellationToken cancellationToken ) { @@ -631,57 +622,11 @@ CancellationToken cancellationToken return NotFound(); if (!await AuthorizeIsOwnerAsync(engine)) return Forbid(); - IEnumerable pretranslations = await _pretranslationService.GetAllAsync( - id, - engine.ModelRevision, - corpusId, - cancellationToken - ); - if (pretranslations is null || pretranslations.Count() == 0) + if (!engine.Corpora.Any(c => c.Id == corpusId)) return NotFound(); - if (!await IsBuilt(id)) + if (engine.ModelRevision == 0) return Conflict(); - return Ok((pretranslations).Select(Map)); - } - /// - /// Gets all pretranslations from a TextId - /// - /// - /// Similar to "get all pretranslations of a corpus," except that the results are filtered by TextId. - /// - /// The translation engine id - /// The corpus id - /// The text id - /// - /// The pretranslations - /// The client is not authenticated - /// The authenticated client cannot perform the operation or does not own the translation engine - /// The engine or corpus or text does not exist - /// The method is not supported - /// The engine needs to be built first - /// A necessary service is currently unavailable. Check `/health` for more details. - [Authorize(Scopes.ReadTranslationEngines)] - [HttpGet("{id}/corpora/{corpusId}/pretranslations/{textId}")] - [ProducesResponseType(StatusCodes.Status200OK)] - [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] - [ProducesResponseType(typeof(void), StatusCodes.Status403Forbidden)] - [ProducesResponseType(typeof(void), StatusCodes.Status404NotFound)] - [ProducesResponseType(typeof(void), StatusCodes.Status405MethodNotAllowed)] - [ProducesResponseType(typeof(void), StatusCodes.Status409Conflict)] - [ProducesResponseType(typeof(void), StatusCodes.Status503ServiceUnavailable)] - public async Task>> GetAllPretranslationsAsync( - [NotNull] string id, - [NotNull] string corpusId, - [NotNull] string textId, - CancellationToken cancellationToken - ) - { - Engine? engine = await _engineService.GetAsync(id, cancellationToken); - if (engine == null) - return NotFound(); - if (!await AuthorizeIsOwnerAsync(engine)) - return Forbid(); IEnumerable pretranslations = await _pretranslationService.GetAllAsync( id, engine.ModelRevision, @@ -689,11 +634,7 @@ CancellationToken cancellationToken textId, cancellationToken ); - if (pretranslations is null || pretranslations.Count() == 0) - return NotFound(); - if (!await IsBuilt(id)) - return Conflict(); - return Ok((pretranslations).Select(Map)); + return Ok(pretranslations.Select(Map)); } /// @@ -1186,10 +1127,4 @@ private TranslationCorpusFileDto Map(CorpusFile source) TextId = source.TextId }; } - - private async Task IsBuilt(string id) - { - IEnumerable builds = await _buildService.GetAllAsync(id); - return builds != null && builds.Any(b => b.State == JobState.Completed); - } } diff --git a/src/Serval.Translation/Services/IPretranslationService.cs b/src/Serval.Translation/Services/IPretranslationService.cs index 762d5ea6..40ea2920 100644 --- a/src/Serval.Translation/Services/IPretranslationService.cs +++ b/src/Serval.Translation/Services/IPretranslationService.cs @@ -6,13 +6,7 @@ Task> GetAllAsync( string engineId, int modelRevision, string corpusId, - CancellationToken cancellationToken = default - ); - Task> GetAllAsync( - string engineId, - int modelRevision, - string corpusId, - string textId, + string? textId = null, CancellationToken cancellationToken = default ); } diff --git a/src/Serval.Translation/Services/PretranslationService.cs b/src/Serval.Translation/Services/PretranslationService.cs index db231b0e..917f1239 100644 --- a/src/Serval.Translation/Services/PretranslationService.cs +++ b/src/Serval.Translation/Services/PretranslationService.cs @@ -9,20 +9,7 @@ public async Task> GetAllAsync( string engineId, int modelRevision, string corpusId, - CancellationToken cancellationToken = default - ) - { - return await Entities.GetAllAsync( - pt => pt.EngineRef == engineId && pt.ModelRevision == modelRevision && pt.CorpusRef == corpusId, - cancellationToken - ); - } - - public async Task> GetAllAsync( - string engineId, - int modelRevision, - string corpusId, - string textId, + string? textId = null, CancellationToken cancellationToken = default ) { @@ -31,7 +18,7 @@ public async Task> GetAllAsync( pt.EngineRef == engineId && pt.ModelRevision == modelRevision && pt.CorpusRef == corpusId - && pt.TextId == textId, + && (textId == null || pt.TextId == textId), cancellationToken ); } diff --git a/tests/Serval.ApiServer.IntegrationTests/TranslationEngineTests.cs b/tests/Serval.ApiServer.IntegrationTests/TranslationEngineTests.cs index bb2a2129..ed5cd037 100644 --- a/tests/Serval.ApiServer.IntegrationTests/TranslationEngineTests.cs +++ b/tests/Serval.ApiServer.IntegrationTests/TranslationEngineTests.cs @@ -7,21 +7,50 @@ namespace Serval.ApiServer; [Category("Integration")] public class TranslationEngineTests { - TestEnvironment? _env; - TranslationCorpusConfig? _testCorpusConfig; - TranslationCorpusConfig? _testCorpusConfigNonEcho; + private readonly TranslationCorpusConfig TestCorpusConfig = + new() + { + Name = "TestCorpus", + SourceLanguage = "en", + TargetLanguage = "en", + SourceFiles = + { + new TranslationCorpusFileConfig { FileId = FILE1_ID, TextId = "all" } + }, + TargetFiles = + { + new TranslationCorpusFileConfig { FileId = FILE2_ID, TextId = "all" } + } + }; + private readonly TranslationCorpusConfig TestCorpusConfigNonEcho = + new() + { + Name = "TestCorpus", + SourceLanguage = "en", + TargetLanguage = "es", + SourceFiles = + { + new TranslationCorpusFileConfig { FileId = FILE1_ID, TextId = "all" } + }, + TargetFiles = + { + new TranslationCorpusFileConfig { FileId = FILE2_ID, TextId = "all" } + } + }; - const string ECHO_ENGINE1_ID = "e00000000000000000000001"; - const string ECHO_ENGINE2_ID = "e00000000000000000000002"; - const string ECHO_ENGINE3_ID = "e00000000000000000000003"; - const string SMT_ENGINE1_ID = "be0000000000000000000001"; - const string NMT_ENGINE1_ID = "ce0000000000000000000001"; - const string FILE1_ID = "f00000000000000000000001"; - const string FILE1_FILENAME = "abcd"; - const string FILE2_ID = "f00000000000000000000002"; - const string FILE2_FILENAME = "efgh"; - const string DOES_NOT_EXIST_ENGINE_ID = "e00000000000000000000004"; - const string DOES_NOT_EXIST_CORPUS_ID = "c00000000000000000000001"; + private const string ECHO_ENGINE1_ID = "e00000000000000000000001"; + private const string ECHO_ENGINE2_ID = "e00000000000000000000002"; + private const string ECHO_ENGINE3_ID = "e00000000000000000000003"; + private const string SMT_ENGINE1_ID = "be0000000000000000000001"; + private const string NMT_ENGINE1_ID = "ce0000000000000000000001"; + private const string FILE1_ID = "f00000000000000000000001"; + private const string FILE1_FILENAME = "abcd"; + private const string FILE2_ID = "f00000000000000000000002"; + private const string FILE2_FILENAME = "efgh"; + private const string DOES_NOT_EXIST_ENGINE_ID = "e00000000000000000000004"; + private const string DOES_NOT_EXIST_CORPUS_ID = "c00000000000000000000001"; + + private TestEnvironment? _env; [SetUp] public async Task SetUp() @@ -97,35 +126,6 @@ public async Task SetUp() Format = Shared.Contracts.FileFormat.Text }; await _env.DataFiles.InsertAllAsync(new[] { srcFile, trgFile }); - - _testCorpusConfig = new TranslationCorpusConfig - { - Name = "TestCorpus", - SourceLanguage = "en", - TargetLanguage = "en", - SourceFiles = - { - new TranslationCorpusFileConfig { FileId = FILE1_ID, TextId = "all" } - }, - TargetFiles = - { - new TranslationCorpusFileConfig { FileId = FILE2_ID, TextId = "all" } - } - }; - _testCorpusConfigNonEcho = new TranslationCorpusConfig - { - Name = "TestCorpus", - SourceLanguage = "en", - TargetLanguage = "es", - SourceFiles = - { - new TranslationCorpusFileConfig { FileId = FILE1_ID, TextId = "all" } - }, - TargetFiles = - { - new TranslationCorpusFileConfig { FileId = FILE2_ID, TextId = "all" } - } - }; } [Test] @@ -283,6 +283,7 @@ string engineId ) { ITranslationEnginesClient client = _env!.CreateClient(scope); + ServalApiException ex; switch (expectedStatusCode) { case 200: @@ -297,14 +298,22 @@ await _env.Builds.InsertAsync( Has.All.EquivalentTo(new[] { Client.TranslationSource.Primary, Client.TranslationSource.Secondary }) ); break; - case 404: case 409: - var ex = Assert.ThrowsAsync(async () => + _env.EchoClient + .TranslateAsync(Arg.Any(), null, null, Arg.Any()) + .Returns(CreateAsyncUnaryCall(StatusCode.Aborted)); + ex = Assert.ThrowsAsync(async () => + { + await client.TranslateAsync(engineId, "This is a test ."); + }); + Assert.That(ex!.StatusCode, Is.EqualTo(expectedStatusCode)); + break; + case 404: + ex = Assert.ThrowsAsync(async () => { await client.TranslateAsync(engineId, "This is a test ."); }); Assert.That(ex!.StatusCode, Is.EqualTo(expectedStatusCode)); - break; default: Assert.Fail("Unanticipated expectedStatusCode. Check test case for typo."); @@ -323,6 +332,7 @@ string engineId ) { ITranslationEnginesClient client = _env!.CreateClient(scope); + ServalApiException ex; switch (expectedStatusCode) { case 200: @@ -342,14 +352,22 @@ await _env.Builds.InsertAsync( Has.All.EquivalentTo(new[] { Client.TranslationSource.Primary, Client.TranslationSource.Secondary }) ); break; - case 404: case 409: - var ex = Assert.ThrowsAsync(async () => + _env.EchoClient + .TranslateAsync(Arg.Any(), null, null, Arg.Any()) + .Returns(CreateAsyncUnaryCall(StatusCode.Aborted)); + ex = Assert.ThrowsAsync(async () => + { + await client.TranslateNAsync(engineId, 1, "This is a test ."); + }); + Assert.That(ex!.StatusCode, Is.EqualTo(expectedStatusCode)); + break; + case 404: + ex = Assert.ThrowsAsync(async () => { await client.TranslateNAsync(engineId, 1, "This is a test ."); }); Assert.That(ex!.StatusCode, Is.EqualTo(expectedStatusCode)); - break; default: Assert.Fail("Unanticipated expectedStatusCode. Check test case for typo."); @@ -368,10 +386,11 @@ string engineId ) { ITranslationEnginesClient client = _env!.CreateClient(scope); + ServalApiException ex; switch (expectedStatusCode) { case 200: - TranslationCorpus addedCorpus = await client.AddCorpusAsync(engineId, _testCorpusConfig!); + TranslationCorpus addedCorpus = await client.AddCorpusAsync(engineId, TestCorpusConfig); await _env.Builds.InsertAsync( new Build { EngineRef = engineId, State = Shared.Contracts.JobState.Completed } ); @@ -382,9 +401,18 @@ await _env.Builds.InsertAsync( Assert.That(wg.Arcs, Has.Count.EqualTo(4)); }); break; - case 404: case 409: - var ex = Assert.ThrowsAsync(async () => + _env.EchoClient + .GetWordGraphAsync(Arg.Any(), null, null, Arg.Any()) + .Returns(CreateAsyncUnaryCall(StatusCode.Aborted)); + ex = Assert.ThrowsAsync(async () => + { + await client.GetWordGraphAsync(engineId, "This is a test ."); + }); + Assert.That(ex!.StatusCode, Is.EqualTo(expectedStatusCode)); + break; + case 404: + ex = Assert.ThrowsAsync(async () => { await client.GetWordGraphAsync(engineId, "This is a test ."); }); @@ -413,18 +441,28 @@ string engineId TargetSegment = "This is a test .", SentenceStart = true }; + ServalApiException ex; switch (expectedStatusCode) { case 200: - TranslationCorpus addedCorpus = await client.AddCorpusAsync(engineId, _testCorpusConfig!); + TranslationCorpus addedCorpus = await client.AddCorpusAsync(engineId, TestCorpusConfig); await _env.Builds.InsertAsync( new Build { EngineRef = engineId, State = Shared.Contracts.JobState.Completed } ); await client.TrainSegmentAsync(engineId, sp); break; - case 404: case 409: - var ex = Assert.ThrowsAsync(async () => + _env.EchoClient + .TrainSegmentPairAsync(Arg.Any(), null, null, Arg.Any()) + .Returns(CreateAsyncUnaryCall(StatusCode.Aborted)); + ex = Assert.ThrowsAsync(async () => + { + await client.TrainSegmentAsync(engineId, sp); + }); + Assert.That(ex!.StatusCode, Is.EqualTo(expectedStatusCode)); + break; + case 404: + ex = Assert.ThrowsAsync(async () => { await client.TrainSegmentAsync(engineId, sp); }); @@ -445,7 +483,7 @@ public async Task AddCorpusToEngineByIdAsync(IEnumerable scope, int expe switch (expectedStatusCode) { case 201: - TranslationCorpus result = await client.AddCorpusAsync(engineId, _testCorpusConfig!); + TranslationCorpus result = await client.AddCorpusAsync(engineId, TestCorpusConfig); Assert.Multiple(() => { Assert.That(result.Name, Is.EqualTo("TestCorpus")); @@ -463,7 +501,7 @@ public async Task AddCorpusToEngineByIdAsync(IEnumerable scope, int expe case 404: var ex = Assert.ThrowsAsync(async () => { - result = await client.AddCorpusAsync(engineId, _testCorpusConfig!); + result = await client.AddCorpusAsync(engineId, TestCorpusConfig); }); Assert.That(ex!.StatusCode, Is.EqualTo(expectedStatusCode)); break; @@ -499,7 +537,7 @@ string engineId switch (expectedStatusCode) { case 200: - TranslationCorpus result = await client.AddCorpusAsync(engineId, _testCorpusConfig!); + TranslationCorpus result = await client.AddCorpusAsync(engineId, TestCorpusConfig); var src = new[] { new TranslationCorpusFileConfig { FileId = FILE2_ID, TextId = "all" } @@ -563,7 +601,7 @@ string engineId switch (expectedStatusCode) { case 200: - TranslationCorpus result = await client.AddCorpusAsync(engineId, _testCorpusConfig!); + TranslationCorpus result = await client.AddCorpusAsync(engineId, TestCorpusConfig); TranslationCorpus resultAfterAdd = (await client.GetAllCorporaAsync(engineId)).First(); Assert.Multiple(() => { @@ -603,7 +641,7 @@ public async Task GetCorpusByIdForEngineByIdAsync( TranslationCorpus? result = null; if (addCorpus) { - result = await client.AddCorpusAsync(engineId, _testCorpusConfig!); + result = await client.AddCorpusAsync(engineId, TestCorpusConfig); } switch (expectedStatusCode) { @@ -647,7 +685,7 @@ string engineId switch (expectedStatusCode) { case 200: - TranslationCorpus result = await client.AddCorpusAsync(engineId, _testCorpusConfig!); + TranslationCorpus result = await client.AddCorpusAsync(engineId, TestCorpusConfig); await client.DeleteCorpusAsync(engineId, result.Id); ICollection resultsAfterDelete = await client.GetAllCorporaAsync(engineId); Assert.That(resultsAfterDelete, Has.Count.EqualTo(0)); @@ -667,171 +705,114 @@ string engineId } [Test] - [TestCase( - new[] { Scopes.ReadTranslationEngines, Scopes.CreateTranslationEngines, Scopes.UpdateTranslationEngines }, - 200, - ECHO_ENGINE1_ID - )] - [TestCase( - new[] { Scopes.ReadTranslationEngines, Scopes.CreateTranslationEngines, Scopes.UpdateTranslationEngines }, - 409, - ECHO_ENGINE1_ID - )] - [TestCase( - new[] { Scopes.ReadTranslationEngines, Scopes.CreateTranslationEngines, Scopes.UpdateTranslationEngines }, - 404, - DOES_NOT_EXIST_ENGINE_ID, - false - )] - [TestCase( - new[] { Scopes.ReadTranslationEngines, Scopes.CreateTranslationEngines, Scopes.UpdateTranslationEngines }, - 404, - ECHO_ENGINE1_ID, - false - )] - public async Task GetAllPretranslationsForCorpusByIdForEngineByIdAsync( - IEnumerable scope, - int expectedStatusCode, - string engineId, - bool addCorpus = true - ) + public async Task GetAllPretranslationsAsync_Exists() { - ITranslationEnginesClient client = _env!.CreateClient(scope); - TranslationCorpus? addedCorpus = null; - if (addCorpus) - { - addedCorpus = await client.AddCorpusAsync(engineId, _testCorpusConfig!); - var pret = new Translation.Models.Pretranslation - { - CorpusRef = addedCorpus.Id, - TextId = "all", - EngineRef = engineId, - Refs = new List { "ref1", "ref2" }, - Translation = "translation" - }; - await _env.Pretranslations.InsertAsync(pret); - } + ITranslationEnginesClient client = _env!.CreateClient(); + TranslationCorpus addedCorpus = await client.AddCorpusAsync(ECHO_ENGINE1_ID, TestCorpusConfig); - switch (expectedStatusCode) + await _env.Engines.UpdateAsync(ECHO_ENGINE1_ID, u => u.Set(e => e.ModelRevision, 1)); + var pret = new Translation.Models.Pretranslation { - case 200: - Assert.That(addCorpus, Is.True, "Check that addCorpus is true - cannot build without added corpus"); - await _env.Builds.InsertAsync( - new Build { EngineRef = engineId, State = Shared.Contracts.JobState.Completed } - ); - ICollection results = await client.GetAllPretranslationsAsync( - engineId, - addedCorpus!.Id - ); - Assert.That(results.First().TextId, Is.EqualTo("all")); - break; - case 404: - case 409: - var ex = Assert.ThrowsAsync(async () => - { - results = await client.GetAllPretranslationsAsync( - engineId, - addCorpus ? addedCorpus!.Id : "cccccccccccccccccccccccc" - ); - }); - Assert.That(ex!.StatusCode, Is.EqualTo(expectedStatusCode)); - break; - default: - Assert.Fail("Unanticipated expectedStatusCode. Check test case for typo."); - break; - } + CorpusRef = addedCorpus.Id, + TextId = "all", + EngineRef = ECHO_ENGINE1_ID, + Refs = new List { "ref1", "ref2" }, + Translation = "translation", + ModelRevision = 1 + }; + await _env.Pretranslations.InsertAsync(pret); + + ICollection results = await client.GetAllPretranslationsAsync( + ECHO_ENGINE1_ID, + addedCorpus.Id + ); + Assert.That(results.All(p => p.TextId == "all"), Is.True); } [Test] - [TestCase( - new[] { Scopes.ReadTranslationEngines, Scopes.CreateTranslationEngines, Scopes.UpdateTranslationEngines }, - 200, - ECHO_ENGINE1_ID, - true, - "all" - )] - [TestCase( - new[] { Scopes.ReadTranslationEngines, Scopes.CreateTranslationEngines, Scopes.UpdateTranslationEngines }, - 409, - ECHO_ENGINE1_ID, - true, - "all" - )] - [TestCase( - new[] { Scopes.ReadTranslationEngines, Scopes.CreateTranslationEngines, Scopes.UpdateTranslationEngines }, - 404, - DOES_NOT_EXIST_ENGINE_ID, - false, - "all" - )] - [TestCase( - new[] { Scopes.ReadTranslationEngines, Scopes.CreateTranslationEngines, Scopes.UpdateTranslationEngines }, - 404, - ECHO_ENGINE1_ID, - false, - "all" - )] - [TestCase( - new[] { Scopes.ReadTranslationEngines, Scopes.CreateTranslationEngines, Scopes.UpdateTranslationEngines }, - 404, - ECHO_ENGINE1_ID, - true, - "not_the_right_id" - )] - public async Task GetPretranslationForTextByIdForCorpusByIdForEngineByIdAsync( - IEnumerable scope, - int expectedStatusCode, - string engineId, - bool addCorpus, - string textId - ) + public void GetAllPretranslationsAsync_EngineDoesNotExist() { - ITranslationEnginesClient client = _env!.CreateClient(scope); - TranslationCorpus? addedCorpus = null; - if (addCorpus) + ITranslationEnginesClient client = _env!.CreateClient(); + + var ex = Assert.ThrowsAsync( + () => client.GetAllPretranslationsAsync(DOES_NOT_EXIST_ENGINE_ID, "cccccccccccccccccccccccc") + ); + Assert.That(ex!.StatusCode, Is.EqualTo(404)); + } + + [Test] + public void GetAllPretranslationsAsync_CorpusDoesNotExist() + { + ITranslationEnginesClient client = _env!.CreateClient(); + + var ex = Assert.ThrowsAsync( + () => client.GetAllPretranslationsAsync(ECHO_ENGINE1_ID, "cccccccccccccccccccccccc") + ); + Assert.That(ex!.StatusCode, Is.EqualTo(404)); + } + + [Test] + public async Task GetAllPretranslationsAsync_EngineNotBuilt() + { + ITranslationEnginesClient client = _env!.CreateClient(); + TranslationCorpus addedCorpus = await client.AddCorpusAsync(ECHO_ENGINE1_ID, TestCorpusConfig); + + var ex = Assert.ThrowsAsync( + () => client.GetAllPretranslationsAsync(ECHO_ENGINE1_ID, addedCorpus.Id) + ); + Assert.That(ex!.StatusCode, Is.EqualTo(409)); + } + + [Test] + public async Task GetAllPretranslationsAsync_TextIdExists() + { + ITranslationEnginesClient client = _env!.CreateClient(); + TranslationCorpus addedCorpus = await client.AddCorpusAsync(ECHO_ENGINE1_ID, TestCorpusConfig); + + await _env.Engines.UpdateAsync(ECHO_ENGINE1_ID, u => u.Set(e => e.ModelRevision, 1)); + var pret = new Translation.Models.Pretranslation { - addedCorpus = await client.AddCorpusAsync(engineId, _testCorpusConfig!); - var pret = new Translation.Models.Pretranslation - { - CorpusRef = addedCorpus.Id, - TextId = "all", //Note that this is not equal to textId necessarily - EngineRef = engineId, - Refs = new List { "ref1", "ref2" }, - Translation = "translation" - }; - await _env.Pretranslations.InsertAsync(pret); - } + CorpusRef = addedCorpus.Id, + TextId = "all", + EngineRef = ECHO_ENGINE1_ID, + Refs = new List { "ref1", "ref2" }, + Translation = "translation", + ModelRevision = 1 + }; + await _env.Pretranslations.InsertAsync(pret); - switch (expectedStatusCode) + ICollection results = await client.GetAllPretranslationsAsync( + ECHO_ENGINE1_ID, + addedCorpus.Id, + "all" + ); + Assert.That(results.All(p => p.TextId == "all"), Is.True); + } + + [Test] + public async Task GetAllPretranslationsAsync_TextIdDoesNotExist() + { + ITranslationEnginesClient client = _env!.CreateClient(); + TranslationCorpus addedCorpus = await client.AddCorpusAsync(ECHO_ENGINE1_ID, TestCorpusConfig); + + await _env.Engines.UpdateAsync(ECHO_ENGINE1_ID, u => u.Set(e => e.ModelRevision, 1)); + var pret = new Translation.Models.Pretranslation { - case 200: - Assert.That(addCorpus, Is.True, "Check that addCorpus is true - cannot build without added corpus"); - await _env.Builds.InsertAsync( - new Build { EngineRef = engineId, State = Shared.Contracts.JobState.Completed } - ); - ICollection results = await client.GetAllPretranslations2Async( - engineId, - addedCorpus!.Id, - textId - ); - Assert.That(results.First().TextId, Is.EqualTo("all")); - break; - case 404: - case 409: - var ex = Assert.ThrowsAsync(async () => - { - results = await client.GetAllPretranslations2Async( - engineId, - addCorpus ? addedCorpus!.Id : "cccccccccccccccccccccccc", - textId - ); - }); - Assert.That(ex!.StatusCode, Is.EqualTo(expectedStatusCode)); - break; - default: - Assert.Fail("Unanticipated expectedStatusCode. Check test case for typo."); - break; - } + CorpusRef = addedCorpus.Id, + TextId = "all", + EngineRef = ECHO_ENGINE1_ID, + Refs = new List { "ref1", "ref2" }, + Translation = "translation", + ModelRevision = 1 + }; + await _env.Pretranslations.InsertAsync(pret); + + ICollection results = await client.GetAllPretranslationsAsync( + ECHO_ENGINE1_ID, + addedCorpus.Id, + "not_the_right_id" + ); + Assert.That(results, Is.Empty); } [Test] @@ -952,7 +933,7 @@ public async Task StartBuildForEngineByIdAsync(IEnumerable scope, int ex switch (expectedStatusCode) { case 201: - TranslationCorpus addedCorpus = await client.AddCorpusAsync(engineId, _testCorpusConfig!); + TranslationCorpus addedCorpus = await client.AddCorpusAsync(engineId, TestCorpusConfig); ptcc = new PretranslateCorpusConfig { CorpusId = addedCorpus.Id, @@ -1084,7 +1065,7 @@ public async Task TryToQueueMultipleBuildsPerSingleUser() ITranslationEnginesClient client = _env!.CreateClient(); var engineId = NMT_ENGINE1_ID; var expectedStatusCode = 409; - TranslationCorpus addedCorpus = await client.AddCorpusAsync(engineId, _testCorpusConfigNonEcho!); + TranslationCorpus addedCorpus = await client.AddCorpusAsync(engineId, TestCorpusConfigNonEcho); var ptcc = new PretranslateCorpusConfig { CorpusId = addedCorpus.Id, @@ -1106,7 +1087,7 @@ public void AddCorpusWithSameSourceAndTargetLangs() ITranslationEnginesClient client = _env!.CreateClient(); var ex = Assert.ThrowsAsync(async () => { - await client.AddCorpusAsync(NMT_ENGINE1_ID, _testCorpusConfig!); + await client.AddCorpusAsync(NMT_ENGINE1_ID, TestCorpusConfig); }); Assert.That(ex, Is.Not.Null); Assert.That(ex.StatusCode, Is.EqualTo(422)); @@ -1118,6 +1099,29 @@ public void TearDown() _env!.Dispose(); } + private static AsyncUnaryCall CreateAsyncUnaryCall(StatusCode statusCode) + { + var status = new Status(statusCode, string.Empty); + return new AsyncUnaryCall( + Task.FromException(new RpcException(status)), + Task.FromResult(new Metadata()), + () => status, + () => new Metadata(), + () => { } + ); + } + + private static AsyncUnaryCall CreateAsyncUnaryCall(TResponse response) + { + return new AsyncUnaryCall( + Task.FromResult(response), + Task.FromResult(new Metadata()), + () => Status.DefaultSuccess, + () => new Metadata(), + () => { } + ); + } + private class TestEnvironment : DisposableBase { private readonly IServiceScope _scope; @@ -1137,6 +1141,151 @@ public TestEnvironment() IRepository >(); Builds = _scope.ServiceProvider.GetRequiredService>(); + + EchoClient = Substitute.For(); + EchoClient + .CreateAsync(Arg.Any(), null, null, Arg.Any()) + .Returns(CreateAsyncUnaryCall(new Empty())); + EchoClient + .DeleteAsync(Arg.Any(), null, null, Arg.Any()) + .Returns(CreateAsyncUnaryCall(new Empty())); + EchoClient + .StartBuildAsync(Arg.Any(), null, null, Arg.Any()) + .Returns(CreateAsyncUnaryCall(new Empty())); + EchoClient + .CancelBuildAsync(Arg.Any(), null, null, Arg.Any()) + .Returns(CreateAsyncUnaryCall(new Empty())); + var wg = new Translation.V1.WordGraph + { + SourceTokens = { "This is a test .".Split() }, + FinalStates = { 4 }, + Arcs = + { + new Translation.V1.WordGraphArc + { + PrevState = 0, + NextState = 1, + Score = 1.0 + }, + new Translation.V1.WordGraphArc + { + PrevState = 1, + NextState = 2, + Score = 1.0 + }, + new Translation.V1.WordGraphArc + { + PrevState = 2, + NextState = 3, + Score = 1.0 + }, + new Translation.V1.WordGraphArc + { + PrevState = 3, + NextState = 4, + Score = 1.0 + } + } + }; + var wgr = new GetWordGraphResponse { WordGraph = wg }; + EchoClient + .TrainSegmentPairAsync(Arg.Any(), null, null, Arg.Any()) + .Returns(CreateAsyncUnaryCall(new Empty())); + EchoClient + .GetWordGraphAsync(Arg.Any(), null, null, Arg.Any()) + .Returns(CreateAsyncUnaryCall(wgr)); + + var translationResult = new Translation.V1.TranslationResult + { + Translation = "This is a test .", + SourceTokens = { "This is a test .".Split() }, + TargetTokens = { "This is a test .".Split() }, + Confidences = { 1.0, 1.0, 1.0, 1.0, 1.0 }, + Sources = + { + new TranslationSources + { + Values = + { + Translation.V1.TranslationSource.Primary, + Translation.V1.TranslationSource.Secondary + } + }, + new TranslationSources + { + Values = + { + Translation.V1.TranslationSource.Primary, + Translation.V1.TranslationSource.Secondary + } + }, + new TranslationSources + { + Values = + { + Translation.V1.TranslationSource.Primary, + Translation.V1.TranslationSource.Secondary + } + }, + new TranslationSources + { + Values = + { + Translation.V1.TranslationSource.Primary, + Translation.V1.TranslationSource.Secondary + } + }, + new TranslationSources + { + Values = + { + Translation.V1.TranslationSource.Primary, + Translation.V1.TranslationSource.Secondary + } + } + }, + Alignment = + { + new Translation.V1.AlignedWordPair { SourceIndex = 0, TargetIndex = 0 }, + new Translation.V1.AlignedWordPair { SourceIndex = 1, TargetIndex = 1 }, + new Translation.V1.AlignedWordPair { SourceIndex = 2, TargetIndex = 2 }, + new Translation.V1.AlignedWordPair { SourceIndex = 3, TargetIndex = 3 }, + new Translation.V1.AlignedWordPair { SourceIndex = 4, TargetIndex = 4 } + }, + Phrases = + { + new Translation.V1.Phrase + { + SourceSegmentStart = 0, + SourceSegmentEnd = 5, + TargetSegmentCut = 5 + } + } + }; + var translateResponse = new TranslateResponse { Results = { translationResult } }; + EchoClient + .TranslateAsync(Arg.Any(), null, null, Arg.Any()) + .Returns(CreateAsyncUnaryCall(translateResponse)); + + NmtClient = Substitute.For(); + NmtClient + .CreateAsync(Arg.Any(), null, null, Arg.Any()) + .Returns(CreateAsyncUnaryCall(new Empty())); + NmtClient + .DeleteAsync(Arg.Any(), null, null, Arg.Any()) + .Returns(CreateAsyncUnaryCall(new Empty())); + NmtClient + .StartBuildAsync(Arg.Any(), null, null, Arg.Any()) + .Returns(CreateAsyncUnaryCall(new Empty())); + NmtClient + .CancelBuildAsync(Arg.Any(), null, null, Arg.Any()) + .Returns(CreateAsyncUnaryCall(new Empty())); + NmtClient + .GetWordGraphAsync(Arg.Any(), null, null, Arg.Any()) + .Returns(CreateAsyncUnaryCall(StatusCode.Unimplemented)); + NmtClient + .TranslateAsync(Arg.Any(), null, null, Arg.Any()) + .Returns(CreateAsyncUnaryCall(StatusCode.Unimplemented)); } ServalWebApplicationFactory Factory { get; } @@ -1144,6 +1293,8 @@ public TestEnvironment() public IRepository DataFiles { get; } public IRepository Pretranslations { get; } public IRepository Builds { get; } + public TranslationEngineApi.TranslationEngineApiClient EchoClient { get; } + public TranslationEngineApi.TranslationEngineApiClient NmtClient { get; } public TranslationEnginesClient CreateClient(IEnumerable? scope = null) { @@ -1159,163 +1310,13 @@ public TranslationEnginesClient CreateClient(IEnumerable? scope = null) { builder.ConfigureTestServices(services => { - var echoClient = Substitute.For(); - echoClient - .CreateAsync(Arg.Any(), null, null, Arg.Any()) - .Returns(CreateAsyncUnaryCall(new Empty())); - echoClient - .DeleteAsync(Arg.Any(), null, null, Arg.Any()) - .Returns(CreateAsyncUnaryCall(new Empty())); - echoClient - .StartBuildAsync(Arg.Any(), null, null, Arg.Any()) - .Returns(CreateAsyncUnaryCall(new Empty())); - echoClient - .CancelBuildAsync(Arg.Any(), null, null, Arg.Any()) - .Returns(CreateAsyncUnaryCall(new Empty())); - var wg = new Translation.V1.WordGraph - { - SourceTokens = { "This is a test .".Split() }, - FinalStates = { 4 }, - Arcs = - { - new Translation.V1.WordGraphArc - { - PrevState = 0, - NextState = 1, - Score = 1.0 - }, - new Translation.V1.WordGraphArc - { - PrevState = 1, - NextState = 2, - Score = 1.0 - }, - new Translation.V1.WordGraphArc - { - PrevState = 2, - NextState = 3, - Score = 1.0 - }, - new Translation.V1.WordGraphArc - { - PrevState = 3, - NextState = 4, - Score = 1.0 - } - } - }; - var wgr = new GetWordGraphResponse { WordGraph = wg }; - echoClient - .TrainSegmentPairAsync( - Arg.Any(), - null, - null, - Arg.Any() - ) - .Returns(CreateAsyncUnaryCall(new Empty())); - echoClient - .GetWordGraphAsync(Arg.Any(), null, null, Arg.Any()) - .Returns(CreateAsyncUnaryCall(wgr)); - - var translationResult = new Translation.V1.TranslationResult - { - Translation = "This is a test .", - SourceTokens = { "This is a test .".Split() }, - TargetTokens = { "This is a test .".Split() }, - Confidences = { 1.0, 1.0, 1.0, 1.0, 1.0 }, - Sources = - { - new TranslationSources - { - Values = - { - Translation.V1.TranslationSource.Primary, - Translation.V1.TranslationSource.Secondary - } - }, - new TranslationSources - { - Values = - { - Translation.V1.TranslationSource.Primary, - Translation.V1.TranslationSource.Secondary - } - }, - new TranslationSources - { - Values = - { - Translation.V1.TranslationSource.Primary, - Translation.V1.TranslationSource.Secondary - } - }, - new TranslationSources - { - Values = - { - Translation.V1.TranslationSource.Primary, - Translation.V1.TranslationSource.Secondary - } - }, - new TranslationSources - { - Values = - { - Translation.V1.TranslationSource.Primary, - Translation.V1.TranslationSource.Secondary - } - } - }, - Alignment = - { - new Translation.V1.AlignedWordPair { SourceIndex = 0, TargetIndex = 0 }, - new Translation.V1.AlignedWordPair { SourceIndex = 1, TargetIndex = 1 }, - new Translation.V1.AlignedWordPair { SourceIndex = 2, TargetIndex = 2 }, - new Translation.V1.AlignedWordPair { SourceIndex = 3, TargetIndex = 3 }, - new Translation.V1.AlignedWordPair { SourceIndex = 4, TargetIndex = 4 } - }, - Phrases = - { - new Translation.V1.Phrase - { - SourceSegmentStart = 0, - SourceSegmentEnd = 5, - TargetSegmentCut = 5 - } - } - }; - var translateResponse = new TranslateResponse { Results = { translationResult } }; - echoClient - .TranslateAsync(Arg.Any(), null, null, Arg.Any()) - .Returns(CreateAsyncUnaryCall(translateResponse)); - - var nmtClient = Substitute.For(); - nmtClient - .CreateAsync(Arg.Any(), null, null, Arg.Any()) - .Returns(CreateAsyncUnaryCall(new Empty())); - nmtClient - .DeleteAsync(Arg.Any(), null, null, Arg.Any()) - .Returns(CreateAsyncUnaryCall(new Empty())); - nmtClient - .StartBuildAsync(Arg.Any(), null, null, Arg.Any()) - .Returns(CreateAsyncUnaryCall(new Empty())); - nmtClient - .CancelBuildAsync(Arg.Any(), null, null, Arg.Any()) - .Returns(CreateAsyncUnaryCall(new Empty())); - nmtClient - .GetWordGraphAsync(Arg.Any(), null, null, Arg.Any()) - .Returns(CreateAsyncUnaryCall(new GetWordGraphResponse(), true)); - nmtClient - .TranslateAsync(Arg.Any(), null, null, Arg.Any()) - .Returns(CreateAsyncUnaryCall(new TranslateResponse(), true)); - var grpcClientFactory = Substitute.For(); grpcClientFactory .CreateClient("Echo") - .Returns(echoClient); + .Returns(EchoClient); grpcClientFactory .CreateClient("Nmt") - .Returns(nmtClient); + .Returns(NmtClient); services.AddSingleton(grpcClientFactory); }); }) @@ -1330,20 +1331,6 @@ public void ResetDatabases() _mongoClient.DropDatabase("serval_test_jobs"); } - private static AsyncUnaryCall CreateAsyncUnaryCall( - TResponse response, - bool raisesError = false //Not functional since doesn't seem to pass through middleware - ) - { - return new AsyncUnaryCall( - Task.FromResult(response), - Task.FromResult(new Metadata()), - () => raisesError ? new Status(StatusCode.Unimplemented, "") : Status.DefaultSuccess, - () => new Metadata(), - () => { } - ); - } - protected override void DisposeManagedResources() { _scope.Dispose();