Skip to content

Commit

Permalink
Added azd compatible live testing (#189)
Browse files Browse the repository at this point in the history
  • Loading branch information
adrianhall authored Jan 10, 2025
1 parent f0fab0b commit 68fb075
Show file tree
Hide file tree
Showing 16 changed files with 643 additions and 378 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,26 @@ The following NuGet packages have been published:
| [CommunityToolkit.Datasync.Server.NSwag] | ![NSwag Library Version][vs-nswag] | ![NSwag Library Downloads][ds-nswag] |
| [CommunityToolkit.Datasync.Server.Swashbuckle] | ![Swashbuckle Library Version][vs-swashbuckle] | ![Swashbuckle Library Downloads][ds-swashbuckle] |

## Running Live Tests

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
Azure account (you can sign up for one for free):

1. Install the [Azure Developer CLI](https://learn.microsoft.com/azure/developer/azure-developer-cli/install-azd)
2. Run `azd up` in a command line.

This script will create several resources. The cost of running those resources is approximately $40/month (US dollars). However, you will only have
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
tests directory which you can use to enable the live testing.

Live testing can be run using the Visual Studio Test Explorer or via `dotnet test`.

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
live tests are not attempted any more.

> **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
> not checked into public GitHub repositories.
## 🌍 Roadmap

Read what we [plan for next iterations](https://github.com/CommunityToolkit/Datasync/milestones), and feel free to ask questions.
Expand Down
37 changes: 37 additions & 0 deletions azure.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/Azure/azure-dev/main/schemas/v1.0/azure.yaml.json

##
## This AZD template will create all the resources necessary to
## test the Community Toolkit/Datasync libraries, plus deploy an
## app service (on free tier) that talks to the AzSQL service
## for the TODO Sample Service
##

name: communitytoolkit-datasync-test-services

hooks:
postprovision:
posix:
interactive: true
shell: sh
run: ./infra/scripts/write-runsettings.sh
windows:
interactive: true
shell: pwsh
run: ./infra/scripts/write-runsettings.ps1

predown:
posix:
interactive: true
shell: sh
run: ./infra/scripts/remove-runsettings.sh
windows:
interactive: true
shell: pwsh
run: ./infra/scripts/remove-runsettings.ps1

services:
todoservice:
language: csharp
project: ./samples/datasync-server/src/Sample.Datasync.Server
host: appservice
54 changes: 54 additions & 0 deletions infra/main.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
targetScope = 'subscription'

@minLength(1)
@maxLength(64)
@description('Name of the the environment which is used to generate a short unique hash used in all resources.')
param environmentName string

@minLength(1)
@description('Primary location for all resources')
param location string

@description('Id of the user or app to assign application roles')
param principalId string = ''

@description('Optional - the SQL Server administrator password. If not provided, the username will be \'appadmin\'.')
param sqlAdminUsername string = 'appadmin'

@secure()
@description('Optional - SQL Server administrator password. If not provided, a random password will be generated.')
param sqlAdminPassword string = newGuid()

/*********************************************************************************/

var resourceToken = toLower(uniqueString(subscription().id, environmentName, location))
var tags = { 'azd-env-name': environmentName }

/*********************************************************************************/

resource rg 'Microsoft.Resources/resourceGroups@2024-07-01' = {
name: 'rg-${environmentName}'
location: location
tags: tags
}

module resources './resources.bicep' = {
name: 'resources'
scope: rg
params: {
location: location
tags: tags
principalId: principalId
resourceToken: resourceToken
serviceName: 'todoservice'
sqlAdminUsername: sqlAdminUsername
sqlAdminPassword: sqlAdminPassword
}
}

/*********************************************************************************/

output AZSQL_CONNECTION_STRING string = resources.outputs.AZSQL_CONNECTIONSTRING
output PGSQL_CONNECTION_STRING string = resources.outputs.PGSQL_CONNECTIONSTRING
output COSMOS_CONNECTION_STRING string = resources.outputs.COSMOS_CONNECTIONSTRING
output SERVICE_ENDPOINT string = resources.outputs.SERVICE_ENDPOINT
21 changes: 21 additions & 0 deletions infra/main.parameters.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"environmentName": {
"value": "${AZURE_ENV_NAME}"
},
"location": {
"value": "${AZURE_LOCATION}"
},
"principalId": {
"value": "${AZURE_PRINCIPAL_ID}"
},
"sqlAdminUsername": {
"value": "appadmin"
},
"sqlAdminPassword": {
"value": "$(secretOrRandomPassword)"
}
}
}
104 changes: 104 additions & 0 deletions infra/modules/appservice.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
targetScope = 'resourceGroup'

@minLength(1)
@description('The name of the App Service Plan resource')
param appServicePlanName string

@minLength(1)
@description('The name of the App Service resource')
param appServiceName string

@minLength(1)
@description('The name of the test database to create')
param databaseName string = 'tododb'

@minLength(1)
@description('Primary location for all resources')
param location string = resourceGroup().location

@description('The name of the deployment in azure.yaml')
param serviceName string = 'todoservice'

@description('The name of the SQL Server to create.')
param sqlServerName string

@description('Optional - the SQL Server administrator password. If not provided, the username will be \'appadmin\'.')
param sqlAdminUsername string = 'appadmin'

@secure()
@description('Optional - SQL Server administrator password. If not provided, a random password will be generated.')
param sqlAdminPassword string = newGuid()

@description('The list of tags to apply to all resources.')
param tags object = {}

/*********************************************************************************/

resource azsql_server 'Microsoft.Sql/servers@2024-05-01-preview' existing = {
name: sqlServerName
}

resource sqldb 'Microsoft.Sql/servers/databases@2024-05-01-preview' = {
name: databaseName
parent: azsql_server
location: location
tags: tags
sku: {
name: 'Basic'
}
properties: {
collation: 'SQL_Latin1_General_CP1_CI_AS'
maxSizeBytes: 1073741824
}
}

resource appsvc_plan 'Microsoft.Web/serverfarms@2024-04-01' = {
name: appServicePlanName
location: location
tags: tags
sku: {
name: 'B1'
capacity: 1
}
}

resource app_service 'Microsoft.Web/sites@2024-04-01' = {
name: appServiceName
location: location
tags: union(tags, {
'azd-service-name': serviceName
'hidden-related:${appsvc_plan.id}': 'empty'
})
properties: {
httpsOnly: true
serverFarmId: appsvc_plan.id
siteConfig: {
ftpsState: 'Disabled'
minTlsVersion: '1.2'
}
}

resource configLogs 'config' = {
name: 'logs'
properties: {
applicationLogs: { fileSystem: { level: 'Verbose' } }
detailedErrorMessages: { enabled: true }
failedRequestsTracing: { enabled: true }
httpLogs: { fileSystem: { retentionInMb: 35, retentionInDays: 3, enabled: true } }
}
}

resource connectionStrings 'config' = {
name: 'connectionstrings'
properties: {
DefaultConnection: {
value: 'Data Source=tcp:${azsql_server.properties.fullyQualifiedDomainName},1433;Initial Catalog=${sqldb.name};User Id=${sqlAdminUsername};Password=${sqlAdminPassword};'
type: 'SQLAzure'
}
}
}
}

/*********************************************************************************/

output SERVICE_ENDPOINT string = 'https://${app_service.properties.defaultHostName}'
78 changes: 78 additions & 0 deletions infra/modules/azuresql.bicep
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
targetScope = 'resourceGroup'

@description('The list of firewall rules to install')
param firewallRules FirewallRule[] = [
{ startIpAddress: '0.0.0.0', endIpAddress: '0.0.0.0' }
]

@minLength(1)
@description('The name of the test database to create')
param databaseName string = 'unittests'

@minLength(1)
@description('Primary location for all resources')
param location string = resourceGroup().location

@description('The name of the SQL Server to create.')
param sqlServerName string

@description('Optional - the SQL Server administrator password. If not provided, the username will be \'appadmin\'.')
param sqlAdminUsername string = 'appadmin'

@secure()
@description('Optional - SQL Server administrator password. If not provided, a random password will be generated.')
param sqlAdminPassword string = newGuid()

@description('The list of tags to apply to all resources.')
param tags object = {}

/*********************************************************************************/

resource azsql_server 'Microsoft.Sql/servers@2024-05-01-preview' = {
name: sqlServerName
location: location
tags: tags
properties: {
version: '12.0'
minimalTlsVersion: '1.2'
publicNetworkAccess: 'Enabled'
administratorLogin: sqlAdminUsername
administratorLoginPassword: sqlAdminPassword
}

resource fw 'firewallRules' = [
for fwRule in firewallRules: {
name: '${fwRule.startIpAddress}-${fwRule.endIpAddress}'
properties: {
startIpAddress: fwRule.startIpAddress
endIpAddress: fwRule.endIpAddress
}
}
]
}

resource azsql_database 'Microsoft.Sql/servers/databases@2024-05-01-preview' = {
name: databaseName
parent: azsql_server
location: location
tags: tags
sku: {
name: 'Basic'
tier: 'Basic'
}
properties: {
collation: 'SQL_Latin1_General_CP1_CI_AS'
}
}

/*********************************************************************************/

#disable-next-line outputs-should-not-contain-secrets
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'

/*********************************************************************************/

type FirewallRule = {
startIpAddress: string
endIpAddress: string
}
Loading

0 comments on commit 68fb075

Please sign in to comment.