diff --git a/.github/workflows/ci+cd.yml b/.github/workflows/ci+cd.yml
index b7a8733c..15a61084 100644
--- a/.github/workflows/ci+cd.yml
+++ b/.github/workflows/ci+cd.yml
@@ -21,10 +21,14 @@ jobs:
build-and-test:
runs-on: ${{ matrix.os }}
+ timeout-minutes: 360
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest]
+ concurrency:
+ group: ${{ github.workflow }}-${{ github.ref }}-${{ matrix.os }}
+ cancel-in-progress: true
steps:
- name: Checkout
diff --git a/src/LibChorus/VcsDrivers/Mercurial/HgResumeRestApiServer.cs b/src/LibChorus/VcsDrivers/Mercurial/HgResumeRestApiServer.cs
index 1600acf0..5001c6d8 100644
--- a/src/LibChorus/VcsDrivers/Mercurial/HgResumeRestApiServer.cs
+++ b/src/LibChorus/VcsDrivers/Mercurial/HgResumeRestApiServer.cs
@@ -33,10 +33,15 @@ public HgResumeApiResponse Execute(string method, HgResumeApiParameters request,
[Obsolete] public string UserName => null;
[Obsolete] public string Password => null;
- public HgResumeApiResponse Execute(string method, HgResumeApiParameters parameters, byte[] contentToSend, int secondsBeforeTimeout)
+ public static string FormatUrl(Uri uri, string method, HgResumeApiParameters parameters)
{
string queryString = parameters.BuildQueryString();
- Url = string.Format("{0}://{1}/api/v{2}/{3}?{4}", _url.Scheme, _url.Host, ApiVersion, method, queryString);
+ return $"{uri.GetComponents(UriComponents.SchemeAndServer, UriFormat.Unescaped)}/api/v{ApiVersion}/{method}{queryString}";
+ }
+
+ public HgResumeApiResponse Execute(string method, HgResumeApiParameters parameters, byte[] contentToSend, int secondsBeforeTimeout)
+ {
+ Url = FormatUrl(_url, method, parameters);
var req = (HttpWebRequest) WebRequest.Create(Url);
req.UserAgent = $"HgResume v{ApiVersion}";
req.PreAuthenticate = true;
diff --git a/src/LibChorus/VcsDrivers/Mercurial/IApiServer.cs b/src/LibChorus/VcsDrivers/Mercurial/IApiServer.cs
index 93087a9f..974594d5 100644
--- a/src/LibChorus/VcsDrivers/Mercurial/IApiServer.cs
+++ b/src/LibChorus/VcsDrivers/Mercurial/IApiServer.cs
@@ -43,9 +43,13 @@ public HgResumeApiParameters()
// used only by getRevisions API
public int Quantity { get; set; }
+ ///
+ /// returns a query string, prefixed with a '?' unless there are no parameters
+ ///
+ ///
public string BuildQueryString()
{
- string query = "";
+ string query = "?";
if (StartOfWindow >= 0)
{
query += String.Format("offset={0}&", StartOfWindow);
@@ -77,6 +81,10 @@ public string BuildQueryString()
{
query += String.Format("repoId={0}&", RepoId);
}
+ if (query == "?")
+ {
+ return "";
+ }
return query.TrimEnd('&');
}
}
diff --git a/src/LibChorusTests/VcsDrivers/Mercurial/HgResumeRestApiServerTests.cs b/src/LibChorusTests/VcsDrivers/Mercurial/HgResumeRestApiServerTests.cs
index eb07038b..8d8f2904 100644
--- a/src/LibChorusTests/VcsDrivers/Mercurial/HgResumeRestApiServerTests.cs
+++ b/src/LibChorusTests/VcsDrivers/Mercurial/HgResumeRestApiServerTests.cs
@@ -1,3 +1,4 @@
+using System;
using Chorus.VcsDrivers.Mercurial;
using NUnit.Framework;
@@ -21,5 +22,36 @@ public void Constructor_LanguageForgeUrl_IdentityAndProjectIdSetCorrectly()
Assert.That(api.Host, Is.EqualTo("hg.languageforge.org"));
Assert.That(api.ProjectId, Is.EqualTo("kyu-dictionary"));
}
+
+ [Test]
+ public void FormatUrl_FormatsCorrectlyWithEmptyParameters()
+ {
+ Assert.That(HgResumeRestApiServer.FormatUrl(
+ new Uri("https://hg.languageforge.org:1234/projects/kyu-dictionary"),
+ "foo",
+ new HgResumeApiParameters()),
+ Is.EqualTo("https://hg.languageforge.org:1234/api/v03/foo"));
+ }
+
+ [Test]
+ public void FormatUrl_FormatsCorrectlyWithParameters()
+ {
+ //this test is overly specific as the order of the parameters is not important, however this is much simpler to write.
+ Assert.That(HgResumeRestApiServer.FormatUrl(
+ new Uri("https://hg.languageforge.org:1234/projects/kyu-dictionary"),
+ "foo",
+ new HgResumeApiParameters
+ {
+ StartOfWindow = 10,
+ ChunkSize = 20,
+ BundleSize = 30,
+ Quantity = 40,
+ TransId = "transId",
+ BaseHashes = new[] { "baseHash1", "baseHash2" },
+ RepoId = "repoId"
+ }),
+ Is.EqualTo(
+ "https://hg.languageforge.org:1234/api/v03/foo?offset=10&chunkSize=20&bundleSize=30&quantity=40&transId=transId&baseHashes[]=baseHash1&baseHashes[]=baseHash2&repoId=repoId"));
+ }
}
}