Skip to content

Commit 68fb075

Browse files
authored
Added azd compatible live testing (#189)
1 parent f0fab0b commit 68fb075

16 files changed

+643
-378
lines changed

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,26 @@ The following NuGet packages have been published:
5656
| [CommunityToolkit.Datasync.Server.NSwag] | ![NSwag Library Version][vs-nswag] | ![NSwag Library Downloads][ds-nswag] |
5757
| [CommunityToolkit.Datasync.Server.Swashbuckle] | ![Swashbuckle Library Version][vs-swashbuckle] | ![Swashbuckle Library Downloads][ds-swashbuckle] |
5858

59+
## Running Live Tests
60+
61+
The test suite for the library includes "live tests" against real servers that are not normally run. To run those tests, you will need access to an
62+
Azure account (you can sign up for one for free):
63+
64+
1. Install the [Azure Developer CLI](https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd)
65+
2. Run `azd up` in a command line.
66+
67+
This script will create several resources. The cost of running those resources is approximately $40/month (US dollars). However, you will only have
68+
to run the services for less than an hour, so the cost of testing the library should be minimal. The process will create a `.runsettings` file in the
69+
tests directory which you can use to enable the live testing.
70+
71+
Live testing can be run using the Visual Studio Test Explorer or via `dotnet test`.
72+
73+
Once you have completed running the tests, you can remove the created services using `azd down`. This will also remove the `.runsettings` file so that
74+
live tests are not attempted any more.
75+
76+
> **NOTE**: The `.runsettings` file contains secrets. It should not be checked in. We have added this file to the `.gitignore` to ensure that it is
77+
> not checked into public GitHub repositories.
78+
5979
## 🌍 Roadmap
6080

6181
Read what we [plan for next iterations](https://github.com/CommunityToolkit/Datasync/milestones), and feel free to ask questions.

azure.yaml

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json
2+
3+
##
4+
## This AZD template will create all the resources necessary to
5+
## test the Community Toolkit/Datasync libraries, plus deploy an
6+
## app service (on free tier) that talks to the AzSQL service
7+
## for the TODO Sample Service
8+
##
9+
10+
name: communitytoolkit-datasync-test-services
11+
12+
hooks:
13+
postprovision:
14+
posix:
15+
interactive: true
16+
shell: sh
17+
run: ./infra/scripts/write-runsettings.sh
18+
windows:
19+
interactive: true
20+
shell: pwsh
21+
run: ./infra/scripts/write-runsettings.ps1
22+
23+
predown:
24+
posix:
25+
interactive: true
26+
shell: sh
27+
run: ./infra/scripts/remove-runsettings.sh
28+
windows:
29+
interactive: true
30+
shell: pwsh
31+
run: ./infra/scripts/remove-runsettings.ps1
32+
33+
services:
34+
todoservice:
35+
language: csharp
36+
project: ./samples/datasync-server/src/Sample.Datasync.Server
37+
host: appservice

infra/main.bicep

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
targetScope = 'subscription'
2+
3+
@minLength(1)
4+
@maxLength(64)
5+
@description('Name of the the environment which is used to generate a short unique hash used in all resources.')
6+
param environmentName string
7+
8+
@minLength(1)
9+
@description('Primary location for all resources')
10+
param location string
11+
12+
@description('Id of the user or app to assign application roles')
13+
param principalId string = ''
14+
15+
@description('Optional - the SQL Server administrator password. If not provided, the username will be \'appadmin\'.')
16+
param sqlAdminUsername string = 'appadmin'
17+
18+
@secure()
19+
@description('Optional - SQL Server administrator password. If not provided, a random password will be generated.')
20+
param sqlAdminPassword string = newGuid()
21+
22+
/*********************************************************************************/
23+
24+
var resourceToken = toLower(uniqueString(subscription().id, environmentName, location))
25+
var tags = { 'azd-env-name': environmentName }
26+
27+
/*********************************************************************************/
28+
29+
resource rg 'Microsoft.Resources/resourceGroups@2024-07-01' = {
30+
name: 'rg-${environmentName}'
31+
location: location
32+
tags: tags
33+
}
34+
35+
module resources './resources.bicep' = {
36+
name: 'resources'
37+
scope: rg
38+
params: {
39+
location: location
40+
tags: tags
41+
principalId: principalId
42+
resourceToken: resourceToken
43+
serviceName: 'todoservice'
44+
sqlAdminUsername: sqlAdminUsername
45+
sqlAdminPassword: sqlAdminPassword
46+
}
47+
}
48+
49+
/*********************************************************************************/
50+
51+
output AZSQL_CONNECTION_STRING string = resources.outputs.AZSQL_CONNECTIONSTRING
52+
output PGSQL_CONNECTION_STRING string = resources.outputs.PGSQL_CONNECTIONSTRING
53+
output COSMOS_CONNECTION_STRING string = resources.outputs.COSMOS_CONNECTIONSTRING
54+
output SERVICE_ENDPOINT string = resources.outputs.SERVICE_ENDPOINT

infra/main.parameters.json

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{
2+
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
3+
"contentVersion": "1.0.0.0",
4+
"parameters": {
5+
"environmentName": {
6+
"value": "${AZURE_ENV_NAME}"
7+
},
8+
"location": {
9+
"value": "${AZURE_LOCATION}"
10+
},
11+
"principalId": {
12+
"value": "${AZURE_PRINCIPAL_ID}"
13+
},
14+
"sqlAdminUsername": {
15+
"value": "appadmin"
16+
},
17+
"sqlAdminPassword": {
18+
"value": "$(secretOrRandomPassword)"
19+
}
20+
}
21+
}

infra/modules/appservice.bicep

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
targetScope = 'resourceGroup'
2+
3+
@minLength(1)
4+
@description('The name of the App Service Plan resource')
5+
param appServicePlanName string
6+
7+
@minLength(1)
8+
@description('The name of the App Service resource')
9+
param appServiceName string
10+
11+
@minLength(1)
12+
@description('The name of the test database to create')
13+
param databaseName string = 'tododb'
14+
15+
@minLength(1)
16+
@description('Primary location for all resources')
17+
param location string = resourceGroup().location
18+
19+
@description('The name of the deployment in azure.yaml')
20+
param serviceName string = 'todoservice'
21+
22+
@description('The name of the SQL Server to create.')
23+
param sqlServerName string
24+
25+
@description('Optional - the SQL Server administrator password. If not provided, the username will be \'appadmin\'.')
26+
param sqlAdminUsername string = 'appadmin'
27+
28+
@secure()
29+
@description('Optional - SQL Server administrator password. If not provided, a random password will be generated.')
30+
param sqlAdminPassword string = newGuid()
31+
32+
@description('The list of tags to apply to all resources.')
33+
param tags object = {}
34+
35+
/*********************************************************************************/
36+
37+
resource azsql_server 'Microsoft.Sql/servers@2024-05-01-preview' existing = {
38+
name: sqlServerName
39+
}
40+
41+
resource sqldb 'Microsoft.Sql/servers/databases@2024-05-01-preview' = {
42+
name: databaseName
43+
parent: azsql_server
44+
location: location
45+
tags: tags
46+
sku: {
47+
name: 'Basic'
48+
}
49+
properties: {
50+
collation: 'SQL_Latin1_General_CP1_CI_AS'
51+
maxSizeBytes: 1073741824
52+
}
53+
}
54+
55+
resource appsvc_plan 'Microsoft.Web/serverfarms@2024-04-01' = {
56+
name: appServicePlanName
57+
location: location
58+
tags: tags
59+
sku: {
60+
name: 'B1'
61+
capacity: 1
62+
}
63+
}
64+
65+
resource app_service 'Microsoft.Web/sites@2024-04-01' = {
66+
name: appServiceName
67+
location: location
68+
tags: union(tags, {
69+
'azd-service-name': serviceName
70+
'hidden-related:${appsvc_plan.id}': 'empty'
71+
})
72+
properties: {
73+
httpsOnly: true
74+
serverFarmId: appsvc_plan.id
75+
siteConfig: {
76+
ftpsState: 'Disabled'
77+
minTlsVersion: '1.2'
78+
}
79+
}
80+
81+
resource configLogs 'config' = {
82+
name: 'logs'
83+
properties: {
84+
applicationLogs: { fileSystem: { level: 'Verbose' } }
85+
detailedErrorMessages: { enabled: true }
86+
failedRequestsTracing: { enabled: true }
87+
httpLogs: { fileSystem: { retentionInMb: 35, retentionInDays: 3, enabled: true } }
88+
}
89+
}
90+
91+
resource connectionStrings 'config' = {
92+
name: 'connectionstrings'
93+
properties: {
94+
DefaultConnection: {
95+
value: 'Data Source=tcp:${azsql_server.properties.fullyQualifiedDomainName},1433;Initial Catalog=${sqldb.name};User Id=${sqlAdminUsername};Password=${sqlAdminPassword};'
96+
type: 'SQLAzure'
97+
}
98+
}
99+
}
100+
}
101+
102+
/*********************************************************************************/
103+
104+
output SERVICE_ENDPOINT string = 'https://${app_service.properties.defaultHostName}'

infra/modules/azuresql.bicep

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
targetScope = 'resourceGroup'
2+
3+
@description('The list of firewall rules to install')
4+
param firewallRules FirewallRule[] = [
5+
{ startIpAddress: '0.0.0.0', endIpAddress: '0.0.0.0' }
6+
]
7+
8+
@minLength(1)
9+
@description('The name of the test database to create')
10+
param databaseName string = 'unittests'
11+
12+
@minLength(1)
13+
@description('Primary location for all resources')
14+
param location string = resourceGroup().location
15+
16+
@description('The name of the SQL Server to create.')
17+
param sqlServerName string
18+
19+
@description('Optional - the SQL Server administrator password. If not provided, the username will be \'appadmin\'.')
20+
param sqlAdminUsername string = 'appadmin'
21+
22+
@secure()
23+
@description('Optional - SQL Server administrator password. If not provided, a random password will be generated.')
24+
param sqlAdminPassword string = newGuid()
25+
26+
@description('The list of tags to apply to all resources.')
27+
param tags object = {}
28+
29+
/*********************************************************************************/
30+
31+
resource azsql_server 'Microsoft.Sql/servers@2024-05-01-preview' = {
32+
name: sqlServerName
33+
location: location
34+
tags: tags
35+
properties: {
36+
version: '12.0'
37+
minimalTlsVersion: '1.2'
38+
publicNetworkAccess: 'Enabled'
39+
administratorLogin: sqlAdminUsername
40+
administratorLoginPassword: sqlAdminPassword
41+
}
42+
43+
resource fw 'firewallRules' = [
44+
for fwRule in firewallRules: {
45+
name: '${fwRule.startIpAddress}-${fwRule.endIpAddress}'
46+
properties: {
47+
startIpAddress: fwRule.startIpAddress
48+
endIpAddress: fwRule.endIpAddress
49+
}
50+
}
51+
]
52+
}
53+
54+
resource azsql_database 'Microsoft.Sql/servers/databases@2024-05-01-preview' = {
55+
name: databaseName
56+
parent: azsql_server
57+
location: location
58+
tags: tags
59+
sku: {
60+
name: 'Basic'
61+
tier: 'Basic'
62+
}
63+
properties: {
64+
collation: 'SQL_Latin1_General_CP1_CI_AS'
65+
}
66+
}
67+
68+
/*********************************************************************************/
69+
70+
#disable-next-line outputs-should-not-contain-secrets
71+
output AZSQL_CONNECTIONSTRING string = 'Data Source=tcp:${azsql_server.properties.fullyQualifiedDomainName},1433;Initial Catalog=${azsql_database.name};User Id=${azsql_server.properties.administratorLogin}@${azsql_server.properties.fullyQualifiedDomainName};Password=${sqlAdminPassword};Encrypt=True;TrustServerCertificate=False'
72+
73+
/*********************************************************************************/
74+
75+
type FirewallRule = {
76+
startIpAddress: string
77+
endIpAddress: string
78+
}

0 commit comments

Comments
 (0)