Skip to content

Commit 8dfc5c5

Browse files
committed
Add .NET SDK test templates and test generation
Introduces comprehensive test templates for the .NET SDK, including unit tests for client, models, enums, converters, exceptions, and utility classes. Updates the DotNet language generator to support test file generation and adds new Twig filters and functions to facilitate test code creation.
1 parent 76ce99c commit 8dfc5c5

19 files changed

+2928
-5
lines changed

src/SDK/Language/DotNet.php

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,85 @@ public function getFiles(): array
440440
'scope' => 'default',
441441
'destination' => '{{ spec.title | caseUcfirst }}/Enums/IEnum.cs',
442442
'template' => 'dotnet/Package/Enums/IEnum.cs.twig',
443+
],
444+
// Tests
445+
[
446+
'scope' => 'default',
447+
'destination' => '{{ spec.title | caseUcfirst }}.Tests/{{ spec.title | caseUcfirst }}.Tests.csproj',
448+
'template' => 'dotnet/Package.Tests/Tests.csproj.twig',
449+
],
450+
[
451+
'scope' => 'default',
452+
'destination' => '{{ spec.title | caseUcfirst }}.Tests/.gitignore',
453+
'template' => 'dotnet/Package.Tests/.gitignore',
454+
],
455+
[
456+
'scope' => 'default',
457+
'destination' => '{{ spec.title | caseUcfirst }}.Tests/ClientTests.cs',
458+
'template' => 'dotnet/Package.Tests/ClientTests.cs.twig',
459+
],
460+
[
461+
'scope' => 'default',
462+
'destination' => '{{ spec.title | caseUcfirst }}.Tests/IDTests.cs',
463+
'template' => 'dotnet/Package.Tests/IDTests.cs.twig',
464+
],
465+
[
466+
'scope' => 'default',
467+
'destination' => '{{ spec.title | caseUcfirst }}.Tests/PermissionTests.cs',
468+
'template' => 'dotnet/Package.Tests/PermissionTests.cs.twig',
469+
],
470+
[
471+
'scope' => 'default',
472+
'destination' => '{{ spec.title | caseUcfirst }}.Tests/RoleTests.cs',
473+
'template' => 'dotnet/Package.Tests/RoleTests.cs.twig',
474+
],
475+
[
476+
'scope' => 'default',
477+
'destination' => '{{ spec.title | caseUcfirst }}.Tests/QueryTests.cs',
478+
'template' => 'dotnet/Package.Tests/QueryTests.cs.twig',
479+
],
480+
[
481+
'scope' => 'default',
482+
'destination' => '{{ spec.title | caseUcfirst }}.Tests/ExceptionTests.cs',
483+
'template' => 'dotnet/Package.Tests/ExceptionTests.cs.twig',
484+
],
485+
[
486+
'scope' => 'default',
487+
'destination' => '{{ spec.title | caseUcfirst }}.Tests/UploadProgressTests.cs',
488+
'template' => 'dotnet/Package.Tests/UploadProgressTests.cs.twig',
489+
],
490+
[
491+
'scope' => 'default',
492+
'destination' => '{{ spec.title | caseUcfirst }}.Tests/Models/InputFileTests.cs',
493+
'template' => 'dotnet/Package.Tests/Models/InputFileTests.cs.twig',
494+
],
495+
[
496+
'scope' => 'default',
497+
'destination' => '{{ spec.title | caseUcfirst }}.Tests/Converters/ObjectToInferredTypesConverterTests.cs',
498+
'template' => 'dotnet/Package.Tests/Converters/ObjectToInferredTypesConverterTests.cs.twig',
499+
],
500+
[
501+
'scope' => 'default',
502+
'destination' => '{{ spec.title | caseUcfirst }}.Tests/Converters/ValueClassConverterTests.cs',
503+
'template' => 'dotnet/Package.Tests/Converters/ValueClassConverterTests.cs.twig',
504+
],
505+
// Tests for each definition (model)
506+
[
507+
'scope' => 'definition',
508+
'destination' => '{{ spec.title | caseUcfirst }}.Tests/Models/{{ definition.name | caseUcfirst | overrideIdentifier }}Tests.cs',
509+
'template' => 'dotnet/Package.Tests/Models/ModelTests.cs.twig',
510+
],
511+
// Tests for each enum
512+
[
513+
'scope' => 'enum',
514+
'destination' => '{{ spec.title | caseUcfirst }}.Tests/Enums/{{ enum.name | caseUcfirst | overrideIdentifier }}Tests.cs',
515+
'template' => 'dotnet/Package.Tests/Enums/EnumTests.cs.twig',
516+
],
517+
// Tests for each service
518+
[
519+
'scope' => 'service',
520+
'destination' => '{{ spec.title | caseUcfirst }}.Tests/Services/{{service.name | caseUcfirst}}Tests.cs',
521+
'template' => 'dotnet/Package.Tests/Services/ServiceTests.cs.twig',
443522
]
444523
];
445524
}
@@ -463,6 +542,12 @@ public function getFilters(): array
463542
}
464543
return $property;
465544
}),
545+
new TwigFilter('escapeCsString', function ($value) {
546+
if (is_string($value)) {
547+
return addcslashes($value, '\\"');
548+
}
549+
return $value;
550+
}),
466551
];
467552
}
468553

@@ -495,6 +580,30 @@ public function getFunctions(): array
495580

496581
return $result;
497582
}, ['is_safe' => ['html']]),
583+
new TwigFunction('test_item_type', function (array $property) {
584+
// For test templates: returns the item type for arrays without the List<> wrapper
585+
$result = '';
586+
587+
if (isset($property['sub_schema']) && !empty($property['sub_schema'])) {
588+
// Model type
589+
$result = $this->toPascalCase($property['sub_schema']);
590+
$result = 'Appwrite.Models.' . $result;
591+
} elseif (isset($property['enum']) && !empty($property['enum'])) {
592+
// Enum type
593+
$enumName = $property['enumName'] ?? $property['name'];
594+
$result = 'Appwrite.Enums.' . $this->toPascalCase($enumName);
595+
} elseif (isset($property['items']) && isset($property['items']['type'])) {
596+
// Primitive array type (for definitions)
597+
$result = $this->getTypeName($property['items']);
598+
} elseif (isset($property['array']) && isset($property['array']['type'])) {
599+
// Primitive array type (for method parameters)
600+
$result = $this->getTypeName($property['array']);
601+
} else {
602+
$result = 'object';
603+
}
604+
605+
return $result;
606+
}, ['is_safe' => ['html']]),
498607
new TwigFunction('property_name', function (array $definition, array $property) {
499608
$name = $property['name'];
500609
$name = \str_replace('$', '', $name);
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Test results
2+
TestResults/
3+
*.trx
4+
*.coverage
5+
*.coveragexml
6+
7+
# Coverage reports
8+
coverage/
9+
coverage.json
10+
coverage.opencover.xml
11+
lcov.info
12+
13+
# Build outputs
14+
bin/
15+
obj/
16+
*.user
17+
*.suo
18+
19+
# Rider
20+
.idea/
21+
22+
# Visual Studio
23+
.vs/
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Net.Http;
4+
using System.Threading.Tasks;
5+
using Xunit;
6+
using {{ spec.title | caseUcfirst }};
7+
8+
namespace {{ spec.title | caseUcfirst }}.Tests
9+
{
10+
public class ClientTests
11+
{
12+
[Fact]
13+
public void Constructor_Default_CreatesClient()
14+
{
15+
// Act
16+
var client = new Client();
17+
18+
// Assert
19+
Assert.NotNull(client);
20+
Assert.Equal("{{spec.endpoint}}", client.Endpoint);
21+
Assert.NotNull(client.Config);
22+
}
23+
24+
[Fact]
25+
public void Constructor_WithCustomEndpoint_SetsEndpoint()
26+
{
27+
// Arrange
28+
var customEndpoint = "https://custom.example.com/v1";
29+
30+
// Act
31+
var client = new Client(endpoint: customEndpoint);
32+
33+
// Assert
34+
Assert.Equal(customEndpoint, client.Endpoint);
35+
}
36+
37+
[Fact]
38+
public void Constructor_WithSelfSigned_EnablesSelfSigned()
39+
{
40+
// Act
41+
var client = new Client(selfSigned: true);
42+
43+
// Assert
44+
Assert.NotNull(client);
45+
}
46+
47+
[Fact]
48+
public void Constructor_WithHttpClient_UsesProvidedClient()
49+
{
50+
// Arrange
51+
var httpClient = new HttpClient();
52+
53+
// Act
54+
var client = new Client(http: httpClient);
55+
56+
// Assert
57+
Assert.NotNull(client);
58+
}
59+
60+
[Fact]
61+
public void SetEndpoint_UpdatesEndpoint()
62+
{
63+
// Arrange
64+
var client = new Client();
65+
var newEndpoint = "https://new.example.com/v1";
66+
67+
// Act
68+
var result = client.SetEndpoint(newEndpoint);
69+
70+
// Assert
71+
Assert.Equal(newEndpoint, client.Endpoint);
72+
Assert.Same(client, result);
73+
}
74+
75+
[Theory]
76+
{%~ for header in spec.global.headers %}
77+
[InlineData("{{header.key}}", "test-{{header.key}}")]
78+
{%~ endfor %}
79+
public void SetHeader_SetsCustomHeader(string key, string value)
80+
{
81+
// Arrange
82+
var client = new Client();
83+
84+
// Act
85+
var result = client.AddHeader(key, value);
86+
87+
// Assert
88+
Assert.Same(client, result);
89+
}
90+
91+
[Fact]
92+
public void Config_IsNotNull()
93+
{
94+
// Arrange & Act
95+
var client = new Client();
96+
97+
// Assert
98+
Assert.NotNull(client.Config);
99+
}
100+
101+
[Fact]
102+
public void SetProject_UpdatesConfig()
103+
{
104+
// Arrange
105+
var client = new Client();
106+
var projectId = "test-project-id";
107+
108+
// Act
109+
var result = client.SetProject(projectId);
110+
111+
// Assert
112+
Assert.True(client.Config.ContainsKey("project"));
113+
Assert.Equal(projectId, client.Config["project"]);
114+
Assert.Same(client, result);
115+
}
116+
117+
[Fact]
118+
public void SetSelfSigned_EnablesSelfSignedCertificates()
119+
{
120+
// Arrange
121+
var client = new Client();
122+
123+
// Act
124+
var result = client.SetSelfSigned(true);
125+
126+
// Assert
127+
Assert.NotNull(result);
128+
Assert.Same(client, result);
129+
}
130+
131+
[Fact]
132+
public void SetSelfSigned_DisablesSelfSignedCertificates()
133+
{
134+
// Arrange
135+
var client = new Client(selfSigned: true);
136+
137+
// Act
138+
var result = client.SetSelfSigned(false);
139+
140+
// Assert
141+
Assert.NotNull(result);
142+
Assert.Same(client, result);
143+
}
144+
145+
[Fact]
146+
public void DeserializerOptions_IsNotNull()
147+
{
148+
// Assert
149+
Assert.NotNull(Client.DeserializerOptions);
150+
}
151+
152+
[Fact]
153+
public void SerializerOptions_IsNotNull()
154+
{
155+
// Assert
156+
Assert.NotNull(Client.SerializerOptions);
157+
}
158+
159+
[Fact]
160+
public void DeserializerOptions_HasConverters()
161+
{
162+
// Assert
163+
Assert.NotEmpty(Client.DeserializerOptions.Converters);
164+
}
165+
166+
[Fact]
167+
public void SerializerOptions_HasConverters()
168+
{
169+
// Assert
170+
Assert.NotEmpty(Client.SerializerOptions.Converters);
171+
}
172+
173+
[Fact]
174+
public void Endpoint_CanBeRetrieved()
175+
{
176+
// Arrange
177+
var endpoint = "https://test.example.com/v1";
178+
var client = new Client(endpoint: endpoint);
179+
180+
// Act
181+
var result = client.Endpoint;
182+
183+
// Assert
184+
Assert.Equal(endpoint, result);
185+
}
186+
187+
[Fact]
188+
public void Config_CanBeRetrieved()
189+
{
190+
// Arrange
191+
var client = new Client();
192+
193+
// Act
194+
var config = client.Config;
195+
196+
// Assert
197+
Assert.NotNull(config);
198+
Assert.IsType<Dictionary<string, string>>(config);
199+
}
200+
201+
[Fact]
202+
public void ChainedCalls_Work()
203+
{
204+
// Arrange & Act
205+
var client = new Client()
206+
.SetEndpoint("https://example.com/v1")
207+
.SetProject("test-project")
208+
.SetSelfSigned(false);
209+
210+
// Assert
211+
Assert.NotNull(client);
212+
Assert.Equal("https://example.com/v1", client.Endpoint);
213+
Assert.Equal("test-project", client.Config["project"]);
214+
}
215+
}
216+
}

0 commit comments

Comments
 (0)