Skip to content

Commit

Permalink
Merge pull request #8 from XPing365/feature/add_tutorial
Browse files Browse the repository at this point in the history
Add tutorial.md file
  • Loading branch information
adydecki authored Jan 28, 2024
2 parents 56c6096 + 880cc6b commit cd4c290
Show file tree
Hide file tree
Showing 12 changed files with 332 additions and 381 deletions.
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The library is called <b>XPing365</b>, which stands for eXternal Pings, and is u

To get started with <b>XPing365</b> SDK, please refer to the following resources:

1. [Wiki page](/xping365-sdk/wiki/getting-started.html): The wiki page provides detailed information about how to use <b>XPing365</b> SDK, including installation instructions, usage examples, and more.
1. [Wiki page](/xping365-sdk/wiki/overview.html): The wiki page provides detailed information about how to use <b>XPing365</b> SDK, including installation instructions, usage examples, and more.

2. [API page](/xping365-sdk/api/XPing365.Sdk.Availability.html): The API page provides detailed information about the <b>XPing365</b> API, including class definitions, method signatures, and more.

Expand Down
9 changes: 8 additions & 1 deletion docs/wiki/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,11 @@ TestSession session = await testAgent
.ConfigureAwait(false);
```

That’s it! You’re now ready to start automating your web application tests and monitoring your server’s content using <b>XPing365</b> SDK.
That’s it! You’re now ready to start automating your web application tests and monitoring your server’s content using <b>XPing365</b> SDK.

## Next Steps

To get started with XPing365 SDK, see the following resources:

- [Overview: How does it work?](/docs/wiki/overview.md)
- [Tutorial: Get started with XPing365 SDK](/docs/wiki/tutorial.md)
8 changes: 7 additions & 1 deletion docs/wiki/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,10 @@ Whenever any of these steps fail, <b>XPing365</b> makes it easy to find out the
Below figure provides a high-level overview of the XPing365 architecture.
<br/><br/>

![XPing365 Architecture](./../images/architecture-overview.svg)
![XPing365 Architecture](/xping365-sdk/images/architecture-overview.svg)

## Next Steps

To get started with <b>XPing365</b> SDK, see the following resources:

- [Tutorial: Get started with XPing365 SDK](/xping365-sdk/wiki/tutorial.html)
8 changes: 4 additions & 4 deletions docs/wiki/toc.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
- name: Getting Started
href: getting-started.md
- name: How does it work?
href: overview.md
- name: Overview
href: overview.md
- name: Tutorial - Get started with XPing365 SDK
href: tutorial.md
308 changes: 308 additions & 0 deletions docs/wiki/tutorial.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,308 @@
# Tutorial: Get started with XPing365 SDK

This tutorial demonstrates how to create a .NET console application that utilizes the XPing365.Availability library. You will start by creating a basic test agent and adding a reporting service. Then, you will build upon that foundation by creating a validation pipeline that contains multiple test components which will run test operations to validate server response.

## Prerequisites

- A code editor, such as [Visual Studio Code](https://code.visualstudio.com/) with the [C# extension](https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp).
- The [.NET 8 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/8.0).

Or

- [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/?utm_medium=microsoft&utm_source=learn.microsoft.com&utm_campaign=inline+link&utm_content=download+vs2022) with the _.NET desktop development_ workload installed.

## Create the app

- Create a .NET 8 console app project named "ConsoleApp".

- Create a folder named _ConsoleApp_ for the project, and then open a command prompt in the new folder.

Run the following command:

```console
dotnet new console --framework net8.0
```

## Install the XPing365.Availability package

Run the following command:

```console
dotnet add package XPing365.Availability --prerelease
```

The `--prerelease` option is necessary because the library is still in beta.

Additionally install `CommandLine` package which we will use in our _ConsoleApp_ to parse and handle command line arguments.

```console
dotnet add package System.CommandLine --prerelease
```

For more information on how to use `CommandLine` please follow [Command Line Tutorial](https://learn.microsoft.com/en-us/dotnet/standard/commandline/get-started-tutorial)

#### Replace the content of the Program.cs with the following code

```csharp
class Program
{
const int EXIT_SUCCESS = 0;
const int EXIT_FAILURE = 1;

const int MAX_SIZE_IN_BYTES = 153600; // 150kB
static async Task<int> Main(string[] args)
{
IHost host = CreateHostBuilder(args).Build();

var urlOption = new Option<Uri?>(
name: "--url",
description: "A URL address of the page being validated.")
{ IsRequired = true };

var command = new RootCommand("Sample application for XPing365.Availability");
command.AddOption(urlOption);
command.SetHandler(async (InvocationContext context) =>
{
Uri url = context.ParseResult.GetValueForOption(urlOption)!;
var testAgent = host.Services.GetRequiredService<AvailabilityTestAgent>();

TestSession session = await testAgent
.RunAsync(url, settings: TestSettings.DefaultForAvailability);
context.Console.WriteLine("\nSummary:");
context.Console.WriteLine($"{session}");
context.ExitCode = session.IsValid ? EXIT_SUCCESS : EXIT_FAILURE;
});

return await command.InvokeAsync(args);
}

static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((services) =>
{
services.AddTransient<IProgress<TestStep>, Progress>();
services.AddAvailabilityTestAgent();
})
.ConfigureLogging(logging =>
{
logging.AddFilter(typeof(HttpClient).FullName, LogLevel.Warning);
});
}
```

#### Add reporting mechanism

Create a new class `Progress.cs` and replace its content with following:

```csharp
class Progress(ILogger<Program> logger) : IProgress<TestStep>
{
private readonly ILogger<Program> _logger = logger;

public void Report(TestStep value)
{
switch (value.Result)
{
case TestStepResult.Succeeded:
_logger.LogInformation("{Value}", value.ToString());
break;
case TestStepResult.Failed:
_logger.LogError("{Value}", value.ToString());
break;
}
}
}
```

The `IProgress<TestStep>` interface is implemented by this class, which is called on every test step performed by `AvailabilityTestAgent` during its testing operation. This allows to monitor the progress of the test execution.

The preceding code we added earlier does following:

- Creates a default host builder and adds availability test agent. It also configures logging mechanism to filter logs coming out from `HttpClient`.

```csharp
static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureServices((services) =>
{
services.AddTransient<IProgress<TestStep>, Progress>();
services.AddAvailabilityTestAgent();
})
.ConfigureLogging(logging =>
{
logging.AddFilter(typeof(HttpClient).FullName, LogLevel.Warning);
});
```

For more information on how to use dependency injection in .NET please follow this [Dependency Injection Tutorial](https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection-usage).

- Creates an option named `--url` of type `Uri` and assigns it to the root command:

```csharp
var urlOption = new Option<Uri?>(
name: "--url",
description: "A URL address of the page being validated.")
{ IsRequired = true };

var command = new RootCommand("Sample application for XPing365.Availability");
command.AddOption(urlOption);
```

- Specifies the handler method that will be called when the root command is invoked and parses the `url` option:

```csharp
command.SetHandler(async (InvocationContext context) =>
{
Uri url = context.ParseResult.GetValueForOption(urlOption)!;

(...)
});

return await command.InvokeAsync(args);
```

- Handler method retrieves `AvailabilityTestAgent` service and runs availability test operations with default test settings against the `url` value:

```csharp

command.SetHandler(async (InvocationContext context) =>
{
(...)

var testAgent = host.Services.GetRequiredService<AvailabilityTestAgent>();

TestSession session = await testAgent
.RunAsync(url, settings: TestSettings.DefaultForAvailability);
});

return await command.InvokeAsync(args);
```

- Prints out summary and specifies exit code depending on the test results:

```csharp
command.SetHandler(async (InvocationContext context) =>
{
(...)

context.Console.WriteLine("\nSummary:");
context.Console.WriteLine($"{session}");
context.ExitCode = session.IsValid ? EXIT_SUCCESS : EXIT_FAILURE;
});

return await command.InvokeAsync(args);
```

## Test the app

Run the `dotnet build` command, and then open a command prompt in the `ConsoleApp/bin/Debug/net8.0` folder to run the executable:

```console
dotnet build
cd bin/Debug/net8.0
ConsoleApp --url https://demoblaze.com
```

Upon running the application, it performs availability tests on the URL specified by the `--url` option and prints the results:

```console
ConsoleApp.exe --url http://demoblaze.com

info: ConsoleApp.Program[0]
1/28/2024 4:37:36 PM (45ms) [ActionStep] DNS lookup succeeded.
info: ConsoleApp.Program[0]
1/28/2024 4:37:36 PM (11ms) [ActionStep] IPAddress accessibility check succeeded.
info: ConsoleApp.Program[0]
1/28/2024 4:37:36 PM (488ms) [ActionStep] Send HTTP Request succeeded.

Summary:
1/28/2024 4:37:36 PM (545.8241[ms]) Test session completed for http://demoblaze.com/.
Total steps: 3, Success: 3, Failures: 0
```

## Add validation pipeline

- Add the following code to the `Program` class in the `Program.cs` file to create a `Pipeline` object that consists of the following validation tests:

```csharp
static Pipeline CreateValidationPipeline() =>
new(components: [
new HttpStatusCodeValidator(
isValid: (HttpStatusCode code) => code == HttpStatusCode.OK,
errorMessage: (HttpStatusCode code) =>
$"The HTTP request failed with status code {code}"),

new HttpResponseHeadersValidator(
isValid: (HttpResponseHeaders headers) => headers.Contains(HeaderNames.Server),
errorMessage: (HttpResponseHeaders headers) =>
$"The HTTP response headers did not include the expected $'{HeaderNames.Server}' header."),

new ServerContentResponseValidator(
isValid: (byte[] content, HttpContentHeaders contentHeaders) => content.Length < MAX_SIZE_IN_BYTES,
errorMessage: (byte[] content, HttpContentHeaders contentHeaders) =>
$"The HTTP response content exceeded the maximum allowed size of {MAX_SIZE_IN_BYTES} bytes.")]);
```

- In the command handler reference the newly added `Pipeline` in the `AvailabilityTestAgent` as follows:

```csharp
testAgent.Container.AddComponent(CreateValidationPipeline());
```

- `HttpStatusCodeValidator` is used to validate the HTTP status code:

```csharp
new HttpStatusCodeValidator(
isValid: (HttpStatusCode code) => code == HttpStatusCode.OK,
errorMessage: (HttpStatusCode code) =>
$"The HTTP request failed with status code {code}")
```

- `HttpResponseHeadersValidator` is used to validate the response headers:

```csharp
new HttpResponseHeadersValidator(
isValid: (HttpResponseHeaders headers) => headers.Contains(HeaderNames.Server),
errorMessage: (HttpResponseHeaders headers) =>
$"The HTTP response headers did not include the expected $'{HeaderNames.Server}' header.")
```

- `ServerContentResponseValidtor` is used to validate the response content:

```csharp
new ServerContentResponseValidator(
isValid: (byte[] content, HttpContentHeaders contentHeaders) => content.Length < MAX_SIZE_IN_BYTES,
errorMessage: (byte[] content, HttpContentHeaders contentHeaders) =>
$"The HTTP response content exceeded the maximum allowed size of {MAX_SIZE_IN_BYTES} bytes.")
```


## Test the new app with validation pipeline

Now if you try to run the app, you get additional test steps performed by the `AvailabilityTestAgent` in the order in which they were added:

```console
ConsoleApp.exe --url http://demoblaze.com

info: ConsoleApp.Program[0]
1/28/2024 4:37:36 PM (45ms) [ActionStep] DNS lookup succeeded.
info: ConsoleApp.Program[0]
1/28/2024 4:37:36 PM (11ms) [ActionStep] IPAddress accessibility check succeeded.
info: ConsoleApp.Program[0]
1/28/2024 4:37:36 PM (488ms) [ActionStep] Send HTTP Request succeeded.
info: ConsoleApp.Program[0]
1/28/2024 4:37:37 PM (0ms) [ValidateStep] Http status code validation succeeded.
info: ConsoleApp.Program[0]
1/28/2024 4:37:37 PM (1ms) [ValidateStep] Http response headers validation succeeded.
info: ConsoleApp.Program[0]
1/28/2024 4:37:37 PM (0ms) [ValidateStep] Server content response validation succeeded.

Summary:
1/28/2024 4:37:36 PM (545.8241[ms]) Test session completed for http://demoblaze.com/.
Total steps: 6, Success: 6, Failures: 0
```

Congratulations! You have successfully completed this tutorial on how to use the `XPing365.Availability` SDK. You have learned how to create a .NET console application that utilizes the `XPing365.Availability` library to automate web application testing. You have also learned how to create a basic test agent, add a reporting service, and build a validation pipeline that contains multiple test components.

For a complete implementation of this tutorial, please refer to our sample folder in our [repository on GitHub](https://github.com/XPing365/xping365-sdk/tree/main/samples/ConsoleApp).
6 changes: 4 additions & 2 deletions src/XPing365.Sdk.Availability/AvailabilityTestAgent.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using XPing365.Sdk.Availability.TestSteps;
using XPing365.Sdk.Core;
using XPing365.Sdk.Core.Components;
using XPing365.Sdk.Core.Components.Session;

namespace XPing365.Sdk.Availability;

Expand All @@ -9,8 +10,8 @@ namespace XPing365.Sdk.Availability;
/// perform availability tests. This class consist of following action test steps:
/// <see cref="DnsLookup"/>, <see cref="IPAddressAccessibilityCheck"/> and <see cref="SendHttpRequest"/> to perform the
/// availability tests. All action steps are performed in a specific order, and their results are added as
/// <see cref="TestStep"/> results to the <see cref="TestContext"/> object. Any failures can be retrieved from the
/// <see cref="TestContext.Failures"/> property along with the error description.
/// <see cref="TestStep"/> results to the <see cref="TestSession"/> object. Any failures can be retrieved from the
/// <see cref="TestSession.Failures"/> property along with the error description.
/// </summary>
/// <example>
/// <code>
Expand All @@ -32,6 +33,7 @@ namespace XPing365.Sdk.Availability;
/// </code>
/// </example>
/// <param name="httpClientFactory"><see cref="IHttpClientFactory"/> implementation instance.</param>
/// <param name="serviceProvider">An instance object of a mechanism for retrieving a service object.</param>
public sealed class AvailabilityTestAgent(IHttpClientFactory httpClientFactory, IServiceProvider serviceProvider) :
TestAgent(serviceProvider, new Pipeline(name: PipelineName, [
new DnsLookup(),
Expand Down
Loading

0 comments on commit cd4c290

Please sign in to comment.