Skip to content

Commit 95e5c6b

Browse files
Add Allure Testops statuses (#53)
* Allure TestOps: add statuses. * Allure TestOps: Add a description field.
1 parent 5ccebc5 commit 95e5c6b

27 files changed

+298
-144
lines changed

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ All notable changes to this project will be documented in this file.
44
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
55
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
66

7-
[3.14.0] - 2022-08-26
7+
[3.15.0] - 2022-09-02
88

99
### Added
10-
- Allure TestOps: Now Allure TestOps is supported! Test case name sync is only implemented.
10+
- Allure TestOps: Add statuses info

GherkinSyncTool.Synchronizers.AllureTestOps/AllureSynchronizerModule.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ public class AllureSynchronizerModule : Module
88
{
99
protected override void Load(ContainerBuilder builder)
1010
{
11-
builder.RegisterType<AllureClient>().SingleInstance();
11+
builder.RegisterType<AllureClientWrapper>().SingleInstance();
1212
builder.RegisterType<CaseContentBuilder>().SingleInstance();
1313
}
1414
}

GherkinSyncTool.Synchronizers.AllureTestOps/AllureTestOpsSynchronizer.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,13 @@ public class AllureTestOpsSynchronizer : ISynchronizer
1818
{
1919
private static readonly Logger Log = LogManager.GetLogger(MethodBase.GetCurrentMethod()?.DeclaringType?.Name);
2020
private readonly GherkinSyncToolConfig _gherkinSyncToolConfig = ConfigurationManager.GetConfiguration<GherkinSyncToolConfig>();
21-
private readonly AllureClient _allureClient;
21+
private readonly AllureClientWrapper _allureClientWrapper;
2222
private readonly Context _context;
2323
private readonly CaseContentBuilder _caseContentBuilder;
2424

25-
public AllureTestOpsSynchronizer(AllureClient allureClient, Context context, CaseContentBuilder caseContentBuilder)
25+
public AllureTestOpsSynchronizer(AllureClientWrapper allureClientWrapper, Context context, CaseContentBuilder caseContentBuilder)
2626
{
27-
_allureClient = allureClient;
27+
_allureClientWrapper = allureClientWrapper;
2828
_context = context;
2929
_caseContentBuilder = caseContentBuilder;
3030
}
@@ -34,7 +34,7 @@ public void Sync(List<IFeatureFile> featureFiles)
3434
var stopwatch = Stopwatch.StartNew();
3535
Log.Info("# Start synchronization with Allure TestOps");
3636

37-
var allureTestCases = _allureClient.GetAllTestCases().ToList();
37+
var allureTestCases = _allureClientWrapper.GetAllTestCases().ToList();
3838

3939
foreach (var featureFile in featureFiles)
4040
{
@@ -79,7 +79,7 @@ public void Sync(List<IFeatureFile> featureFiles)
7979
{
8080
try
8181
{
82-
_allureClient.UpdateTestCase(allureTestCase, caseRequest);
82+
_allureClientWrapper.UpdateTestCase(allureTestCase, caseRequest);
8383
}
8484
catch (AllureException e)
8585
{
@@ -99,7 +99,7 @@ private TestCase CreateTestCase(CreateTestCaseRequest caseRequest)
9999
TestCase result = null;
100100
try
101101
{
102-
result = _allureClient.AddTestCase(caseRequest);
102+
result = _allureClientWrapper.AddTestCase(caseRequest);
103103
return result;
104104
}
105105
catch (AllureException e)

GherkinSyncTool.Synchronizers.AllureTestOps/Client/AllureClient.cs renamed to GherkinSyncTool.Synchronizers.AllureTestOps/Client/AllureClientWrapper.cs

Lines changed: 44 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
using System.Collections.Generic;
33
using System.Reflection;
44
using System.Threading.Tasks;
5-
using GherkinSyncTool.Models;
65
using GherkinSyncTool.Models.Configuration;
76
using GherkinSyncTool.Synchronizers.AllureTestOps.Exception;
87
using GherkinSyncTool.Synchronizers.AllureTestOps.Model;
@@ -15,48 +14,22 @@
1514

1615
namespace GherkinSyncTool.Synchronizers.AllureTestOps.Client
1716
{
18-
public class AllureClient
17+
public class AllureClientWrapper
1918
{
2019
private static readonly Logger Log = LogManager.GetLogger(MethodBase.GetCurrentMethod()?.DeclaringType?.Name);
21-
2220
private readonly AllureTestOpsSettings _azureDevopsSettings =
2321
ConfigurationManager.GetConfiguration<AllureTestOpsConfigs>().AllureTestOpsSettings;
2422

25-
private readonly Context _context;
2623
private readonly IAllureClient _allureClient;
2724

28-
public AllureClient(Context context)
25+
public AllureClientWrapper()
2926
{
30-
_context = context;
31-
_allureClient = RestService.For<IAllureClient>(_azureDevopsSettings.BaseUrl, new RefitSettings
32-
{
33-
AuthorizationHeaderValueGetter = () => Task.FromResult(_azureDevopsSettings.AccessToken),
34-
ContentSerializer = new NewtonsoftJsonContentSerializer(
35-
new JsonSerializerSettings
36-
{
37-
ContractResolver = new CamelCasePropertyNamesContractResolver(),
38-
NullValueHandling = NullValueHandling.Ignore
39-
}
40-
)
41-
42-
});
27+
_allureClient = AllureClient.Get(_azureDevopsSettings.BaseUrl, _azureDevopsSettings.AccessToken);
4328
}
4429

45-
public IEnumerable<Quantori.AllureTestOpsClient.Model.Content> GetAllTestCases()
30+
public IEnumerable<TestCaseContent> GetAllTestCases()
4631
{
47-
var allContent = new List<Quantori.AllureTestOpsClient.Model.Content>();
48-
var isLastElementOnThePage = false;
49-
var page = 0;
50-
while (!isLastElementOnThePage)
51-
{
52-
var response = _allureClient.GetTestCasesAsync(_azureDevopsSettings.ProjectId, page).Result;
53-
ValidateResponse(response);
54-
page++;
55-
isLastElementOnThePage = response.Content!.Last;
56-
allContent.AddRange(response.Content!.Content);
57-
}
58-
59-
return allContent;
32+
return GetAllContent(i => _allureClient.GetTestCasesAsync(_azureDevopsSettings.ProjectId, i).Result);
6033
}
6134

6235
private void ValidateResponse(IApiResponse response)
@@ -82,36 +55,68 @@ public TestCase AddTestCase(CreateTestCaseRequest caseRequest)
8255
return response.Content;
8356
}
8457

85-
8658
public TestCaseOverview GetTestCaseOverview(ulong id)
8759
{
8860
var response = _allureClient.GetTestCaseOverviewAsync(id).Result;
8961
ValidateResponse(response);
9062
return response.Content;
9163
}
9264

93-
public void UpdateTestCase(Quantori.AllureTestOpsClient.Model.Content currentCase, TestCaseRequest caseToUpdate)
65+
public void UpdateTestCase(TestCaseContent currentCase, TestCaseRequest caseToUpdate)
9466
{
9567
if (!IsTestCaseContentEqual(currentCase, caseToUpdate))
9668
{
97-
9869
var response = _allureClient.UpdateTestCaseAsync(currentCase.Id, caseToUpdate).Result;
99-
70+
10071
ValidateResponse(response);
101-
72+
10273
Log.Info($"Updated: [{currentCase.Id}] {caseToUpdate.Name}");
10374
}
10475
else
10576
{
10677
Log.Info($"Up-to-date: [{currentCase.Id}] {currentCase.Name}");
10778
}
10879
}
109-
110-
private static bool IsTestCaseContentEqual(Quantori.AllureTestOpsClient.Model.Content currentCase, TestCaseRequest caseToUpdate)
80+
81+
public IEnumerable<Status> GetAllStatuses()
82+
{
83+
return GetAllContent(i => _allureClient.GetStatusAsync(null,i).Result);
84+
}
85+
86+
public IEnumerable<WorkflowSchema> GetAllWorkflowSchemas(int projectId)
87+
{
88+
return GetAllContent(i => _allureClient.GetWorkflowSchemaAsync(projectId,i).Result);
89+
}
90+
91+
public IEnumerable<WorkflowContent> GetAllWorkflows()
92+
{
93+
return GetAllContent(i => _allureClient.GetWorkflowAsync(i).Result);;
94+
}
95+
96+
private static bool IsTestCaseContentEqual(TestCaseContent currentCase, TestCaseRequest caseToUpdate)
11197
{
11298
if (!currentCase.Name.Equals(caseToUpdate.Name)) return false;
11399
if (!currentCase.Automated.Equals(caseToUpdate.Automated)) return false;
100+
if (!currentCase.Status.Id.Equals(caseToUpdate.StatusId)) return false;
114101
return true;
115102
}
103+
104+
private IEnumerable<T> GetAllContent<T>(Func<int, IApiResponse<GetContentResponse<T>>> function)
105+
{
106+
var allContent = new List<T>();
107+
var isLastElementOnThePage = false;
108+
var page = 0;
109+
while (!isLastElementOnThePage)
110+
{
111+
var response = function(page);
112+
113+
ValidateResponse(response);
114+
page++;
115+
isLastElementOnThePage = response.Content!.Last;
116+
allContent.AddRange(response.Content!.Content);
117+
}
118+
119+
return allContent;
120+
}
116121
}
117122
}

GherkinSyncTool.Synchronizers.AllureTestOps/Content/CaseContentBuilder.cs

Lines changed: 110 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Reflection;
5+
using System.Text;
6+
using Gherkin.Ast;
27
using GherkinSyncTool.Models;
38
using GherkinSyncTool.Models.Configuration;
49
using GherkinSyncTool.Models.Utils;
10+
using GherkinSyncTool.Synchronizers.AllureTestOps.Client;
511
using GherkinSyncTool.Synchronizers.AllureTestOps.Model;
12+
using NLog;
613
using Quantori.AllureTestOpsClient.Model;
714
using Scenario = Gherkin.Ast.Scenario;
815

@@ -13,18 +20,120 @@ public class CaseContentBuilder
1320
private readonly AllureTestOpsSettings _allureTestOpsSettings =
1421
ConfigurationManager.GetConfiguration<AllureTestOpsConfigs>().AllureTestOpsSettings;
1522

23+
private static readonly Logger Log = LogManager.GetLogger(MethodBase.GetCurrentMethod()?.DeclaringType?.Name);
24+
25+
private readonly AllureClientWrapper _allureClientWrapper;
26+
private readonly Context _context;
27+
private List<WorkflowSchema> _workflowSchemas;
28+
private Item _automatedWorkflowId;
29+
private Item _manualWorkflowId;
30+
31+
public List<WorkflowSchema> WorkflowSchemas =>
32+
_workflowSchemas ??= _allureClientWrapper.GetAllWorkflowSchemas(_allureTestOpsSettings.ProjectId).ToList();
33+
34+
private List<WorkflowContent> _workflows;
35+
public List<WorkflowContent> Workflows => _workflows ??= _allureClientWrapper.GetAllWorkflows().ToList();
36+
37+
public Item AutomatedWorkflow =>
38+
_automatedWorkflowId ??= WorkflowSchemas.FirstOrDefault(schema => schema.Type.Equals(TestType.Automated))!.Workflow;
39+
40+
public Item ManualWorkflow => _manualWorkflowId ??= WorkflowSchemas.FirstOrDefault(schema => schema.Type.Equals(TestType.Manual))!.Workflow;
41+
42+
public CaseContentBuilder(AllureClientWrapper allureClientWrapper, Context context)
43+
{
44+
_allureClientWrapper = allureClientWrapper;
45+
_context = context;
46+
}
47+
1648
public CreateTestCaseRequest BuildCaseRequest(Scenario scenario, IFeatureFile featureFile)
1749
{
1850
var caseRequest = new CreateTestCaseRequest
1951
{
2052
Name = scenario.Name,
2153
ProjectId = _allureTestOpsSettings.ProjectId,
22-
Automated = IsAutomated(scenario, featureFile)
54+
Automated = IsAutomated(scenario, featureFile),
55+
StatusId = AddStatus(scenario, featureFile),
56+
WorkflowId = AddWorkflow(scenario, featureFile),
57+
Description = AddDescription(scenario, featureFile)
2358
};
2459

2560
return caseRequest;
2661
}
2762

63+
private string AddDescription(Scenario scenario, IFeatureFile featureFile)
64+
{
65+
var description = new StringBuilder();
66+
description.AppendLine(featureFile.Document.Feature.Description);
67+
description.AppendLine(scenario.Description);
68+
69+
var background = featureFile.Document.Feature.Children.OfType<Background>().FirstOrDefault();
70+
if (background is not null && (!string.IsNullOrWhiteSpace(background.Name) || !string.IsNullOrWhiteSpace(background.Description)))
71+
{
72+
description.AppendLine($"{background.Keyword}: {background.Name}");
73+
description.AppendLine(background.Description);
74+
}
75+
description.Append($"Feature file: {featureFile.RelativePath}");
76+
return description.ToString();
77+
}
78+
79+
private long AddWorkflow(Scenario scenario, IFeatureFile featureFile)
80+
{
81+
return IsAutomated(scenario, featureFile) ? AutomatedWorkflow.Id : ManualWorkflow.Id;
82+
}
83+
84+
private long? AddStatus(Scenario scenario, IFeatureFile featureFile)
85+
{
86+
var allTags = GherkinHelper.GetAllTags(scenario, featureFile);
87+
var statusTag = allTags.LastOrDefault(tag => tag.Name.Contains(TagsConstants.Status, StringComparison.InvariantCultureIgnoreCase));
88+
89+
var automated = IsAutomated(scenario, featureFile);
90+
91+
var manualStatuses = Workflows.FirstOrDefault(workflow => workflow.Id == ManualWorkflow.Id)!.Statuses;
92+
var autoStatuses = Workflows.FirstOrDefault(workflow => workflow.Id == AutomatedWorkflow.Id)!.Statuses;
93+
94+
if (statusTag is null)
95+
{
96+
if (automated)
97+
{
98+
return autoStatuses.FirstOrDefault()!.Id;
99+
}
100+
101+
return manualStatuses.FirstOrDefault()!.Id;
102+
}
103+
104+
var statusString = statusTag.Name.Replace(TagsConstants.Status, "", StringComparison.InvariantCultureIgnoreCase);
105+
106+
107+
if (IsAutomated(scenario, featureFile))
108+
{
109+
try
110+
{
111+
return autoStatuses.First(status => status.Name.Equals(statusString, StringComparison.InvariantCultureIgnoreCase)).Id;
112+
}
113+
catch (InvalidOperationException e)
114+
{
115+
var statusNames = string.Join(", ", autoStatuses.Select(status => status.Name));
116+
Log.Error(e,
117+
$"'{statusString}' is incorrect option for scenario: '{scenario.Name}'. Valid options are: '{statusNames}'. Workflow: '{AutomatedWorkflow.Name}'");
118+
_context.IsRunSuccessful = false;
119+
return autoStatuses.FirstOrDefault()!.Id;
120+
}
121+
}
122+
123+
try
124+
{
125+
return manualStatuses.First(status => status.Name.Equals(statusString, StringComparison.InvariantCultureIgnoreCase)).Id;
126+
}
127+
catch (InvalidOperationException e)
128+
{
129+
var statusNames = string.Join(", ", manualStatuses.Select(status => status.Name));
130+
Log.Error(e,
131+
$"'{statusString}' is incorrect option for scenario: '{scenario.Name}'. Valid options are: '{statusNames}'. Workflow: '{ManualWorkflow.Name}'");
132+
_context.IsRunSuccessful = false;
133+
return manualStatuses.FirstOrDefault()!.Id;
134+
}
135+
}
136+
28137
private bool IsAutomated(Scenario scenario, IFeatureFile featureFile)
29138
{
30139
var allTags = GherkinHelper.GetAllTags(scenario, featureFile);

GherkinSyncTool.Synchronizers.AllureTestOps/GherkinSyncTool.Synchronizers.AllureTestOps.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
<ItemGroup>
99
<PackageReference Include="Autofac" Version="6.3.0" />
1010
<PackageReference Include="NLog" Version="4.7.11" />
11-
<PackageReference Include="Refit.Newtonsoft.Json" Version="6.3.2" />
1211
</ItemGroup>
1312

1413
<ItemGroup>

GherkinSyncTool.Synchronizers.AllureTestOps/Model/TagsConstants.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,6 @@
33
public static class TagsConstants
44
{
55
public const string Automated = "@Automated";
6+
public const string Status = "@Status:";
67
}
78
}

GherkinSyncTool/GherkinSyncTool.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<AssemblyVersion>3.14.0</AssemblyVersion>
4+
<AssemblyVersion>3.15.0</AssemblyVersion>
55
<OutputType>Exe</OutputType>
66
<TargetFrameworks>netcoreapp3.1;net5.0;net6.0</TargetFrameworks>
77
<PackAsTool>true</PackAsTool>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using System.Threading.Tasks;
2+
using Newtonsoft.Json;
3+
using Newtonsoft.Json.Serialization;
4+
using Refit;
5+
6+
namespace Quantori.AllureTestOpsClient
7+
{
8+
public static class AllureClient
9+
{
10+
public static IAllureClient Get(string baseUrl, string accessToken)
11+
{
12+
var allureClient = RestService.For<IAllureClient>(baseUrl, new RefitSettings
13+
{
14+
AuthorizationHeaderValueGetter = () => Task.FromResult(accessToken),
15+
ContentSerializer = new NewtonsoftJsonContentSerializer(
16+
new JsonSerializerSettings
17+
{
18+
ContractResolver = new CamelCasePropertyNamesContractResolver(),
19+
NullValueHandling = NullValueHandling.Ignore
20+
}
21+
)
22+
});
23+
return allureClient;
24+
}
25+
}
26+
}

0 commit comments

Comments
 (0)