Run selenium tests (chrome) in AWS Lambda - NET Core - amazon-web-services

Trying to run selenium tests in AWS Lambda. Read below links and felt confident to implement similar tests with NET Core 2.1.
https://blackboard.github.io/lambda-selenium/
https://medium.com/clog/running-selenium-and-headless-chrome-on-aws-lambda-fb350458e4df
https://aws.amazon.com/blogs/devops/ui-testing-at-scale-with-aws-lambda/
Here is my complete lambda function code.
using System;
using System.Collections.Generic;
using System.IO;
using Amazon.Lambda.Core;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
// Assembly attribute to enable the Lambda function's JSON input to be converted into a .NET class.
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.Json.JsonSerializer))]
namespace selenium_lambda_poc1
{
public class Function
{
/// <summary>
/// A simple function that takes a string and does a ToUpper
/// </summary>
/// <param name="input"></param>
/// <param name="context"></param>
/// <returns></returns>
public void FunctionHandler(string input, ILambdaContext context)
{
context.Logger.LogLine("Starting...");
var driver = GetDriver(context);
driver.Navigate().GoToUrl(input);
var title = driver.Title;
LambdaLogger.Log("Window Title: " + title);
driver.Quit();
context.Logger.LogLine("Ending...");
}
public IWebDriver GetDriver(ILambdaContext context)
{
var lambdaTaskRootPath = Environment.GetEnvironmentVariable("LAMBDA_TASK_ROOT");
context.Logger.LogLine(lambdaTaskRootPath);
Environment.SetEnvironmentVariable("webdriver.chrome.driver", lambdaTaskRootPath + #"/Lib/chromedriver");
ChromeOptions options = new ChromeOptions();
options.BinaryLocation = lambdaTaskRootPath + "/Lib/chrome";
var chromeBinaryPath = options.BinaryLocation;
var chromeDriverPath = lambdaTaskRootPath + #"/Lib/chromedriver";
context.Logger.LogLine("Chrome Path? " + chromeBinaryPath);
context.Logger.LogLine("ChromeDriver Path? " + chromeDriverPath);
context.Logger.LogLine("Chrome - Available? " + File.Exists(chromeBinaryPath).ToString());
context.Logger.LogLine("ChromeDriver - Available? " + File.Exists(chromeDriverPath).ToString());
options.AddArguments(new List<string>() {
"--headless",
"--disable-gpu",
"--single-process",
"--no-sandbox",
"--data-path=/tmp/data-path",
"--homedir=/tmp/homedir",
"--disk-cache-dir=/tmp/cache-dir",
"--allow-file-access-from-files",
"--disable-web-security",
"--disable-extensions",
"--ignore-certificate-errors",
"--disable-ntp-most-likely-favicons-from-server",
"--disable-ntp-popular-sites",
"--disable-infobars",
"--disable-dev-shm-usage",
"--window-size=1366,1024",
"--enable-logging"
});
var driver = new ChromeDriver(lambdaTaskRootPath + "/Lib/", options, TimeSpan.FromMinutes(1));
return driver;
}
}
}
When I test my lambda function, getting the below and I've been struggling with same issue for the last 3 days. Would be grateful if someone can help me out here
Starting ChromeDriver 2.39.562737 (dba483cee6a5f15e2e2d73df16968ab10b38a2bf) on port 38471
Only local connections are allowed.
The HTTP request to the remote WebDriver server for URL http://127.0.0.1:38471/session timed out after 60 seconds.: WebDriverException
at OpenQA.Selenium.Remote.HttpCommandExecutor.MakeHttpRequest(HttpRequestInfo requestInfo)
at OpenQA.Selenium.Remote.HttpCommandExecutor.Execute(Command commandToExecute)
at OpenQA.Selenium.Remote.DriverServiceCommandExecutor.Execute(Command commandToExecute)
at OpenQA.Selenium.Remote.RemoteWebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
at OpenQA.Selenium.Remote.RemoteWebDriver.StartSession(ICapabilities desiredCapabilities)
at OpenQA.Selenium.Remote.RemoteWebDriver..ctor(ICommandExecutor commandExecutor, ICapabilities desiredCapabilities)
at OpenQA.Selenium.Chrome.ChromeDriver..ctor(ChromeDriverService service, ChromeOptions options, TimeSpan commandTimeout)
at selenium_lambda_poc.Function.GetDriver(ILambdaContext context) in C:\Users\sureshraja.s\source\repos\selenium-lambda-poc\selenium-lambda-poc\Function.cs:line 97
at selenium_lambda_poc.Function.FunctionHandler(String input, ILambdaContext context) in C:\Users\sureshraja.s\source\repos\selenium-lambda-poc\selenium-lambda-poc\Function.cs:line 34
at System.Net.HttpWebRequest.GetResponse()
at OpenQA.Selenium.Remote.HttpCommandExecutor.MakeHttpRequest(HttpRequestInfo requestInfo)

Related

Put event in Amazaon Event bus using Jmeter

We are introducing an event bridge to communicate btw 2 components and we want to Performance test the inbound event bus.
We are using jmeter and want to to do a put event in this inbound event bus. has anyone done something like this ?
Probably the most straightforward way is using AWS SDK for Java from JSR223 Test Elements with Groovy
Example code can be found at Working with Amazon EventBridge, I'll add it here just in case
/*
Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/
package com.example.eventbridge;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.eventbridge.EventBridgeClient;
import software.amazon.awssdk.services.eventbridge.model.EventBridgeException;
import software.amazon.awssdk.services.eventbridge.model.PutEventsRequest;
import software.amazon.awssdk.services.eventbridge.model.PutEventsRequestEntry;
import software.amazon.awssdk.services.eventbridge.model.PutEventsResponse;
import software.amazon.awssdk.services.eventbridge.model.PutEventsResultEntry;
import java.util.ArrayList;
import java.util.List;
/**
* To run this Java V2 code example, ensure that you have setup your development environment, including your credentials.
*
* For information, see this documentation topic:
*
* https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
*/
public class PutEvents {
public static void main(String[] args) {
final String USAGE =
"To run this example, supply two resources, identified by Amazon Resource Name (ARN), which the event primarily concerns. " +
"Any number, including zero, may be present. \n" +
"For example: PutEvents <resourceArn> <resourceArn2>\n";
if (args.length != 2) {
System.out.println(USAGE);
System.exit(1);
}
String resourceArn = args[0];
String resourceArn2 = args[1];
Region region = Region.US_WEST_2;
EventBridgeClient eventBrClient = EventBridgeClient.builder()
.region(region)
.build();
putEBEvents(eventBrClient, resourceArn, resourceArn2);
eventBrClient.close();
}
public static void putEBEvents(EventBridgeClient eventBrClient, String resourceArn, String resourceArn2 ) {
try {
// Populate a List with the resource ARN values
List<String> resources = new ArrayList<String>();
resources.add(resourceArn);
resources.add(resourceArn2);
PutEventsRequestEntry reqEntry = PutEventsRequestEntry.builder()
.resources(resources)
.source("com.mycompany.myapp")
.detailType("myDetailType")
.detail("{ \"key1\": \"value1\", \"key2\": \"value2\" }")
.build();
// Add the PutEventsRequestEntry to a list
List<PutEventsRequestEntry> list = new ArrayList<PutEventsRequestEntry>();
list.add(reqEntry);
PutEventsRequest eventsRequest = PutEventsRequest.builder()
.entries(reqEntry)
.build();
PutEventsResponse result = eventBrClient.putEvents(eventsRequest);
for (PutEventsResultEntry resultEntry : result.entries()) {
if (resultEntry.eventId() != null) {
System.out.println("Event Id: " + resultEntry.eventId());
} else {
System.out.println("Injection failed with Error Code: " + resultEntry.errorCode());
}
}
} catch (EventBridgeException e) {
System.err.println(e.awsErrorDetails().errorMessage());
System.exit(1);
}
}
}
Was able to do it by using the os process sampler and using the AWS cli commands.

fetch batch translate job details using SDK

I was trying to find the LanguagePair and Operation associated with a given AWS translate job using the JAVA SDK.
Using the AWS web console, i created a couple of batch jobs to translate a few english sentences to french. In CloudWatch, i could see the metric dimensions as
LanguagePair: en-fr
Operation: TranslateText
Can i retrieve the same information (LanguagePair and Operation) for a given job,
using the TranslateAsyncClient.describeTextTranslationJob(...) method ?
You can use the TranslateClient to describes a translation job given the job number as input. This uses the TranslateClient; however you can use the Async version as well.
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.translate.TranslateClient;
import software.amazon.awssdk.services.translate.model.DescribeTextTranslationJobRequest;
import software.amazon.awssdk.services.translate.model.DescribeTextTranslationJobResponse;
import software.amazon.awssdk.services.translate.model.TranslateException;
// snippet-end:[translate.java2._describe_jobs.import]
/**
* To run this Java V2 code example, ensure that you have setup your development environment, including your credentials.
*
* For information, see this documentation topic:
*
* https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
*/
public class DescribeTextTranslationJob {
public static void main(String[] args) {
final String USAGE = "\n" +
"Usage:\n" +
" DescribeTextTranslationJob <id> \n\n" +
"Where:\n" +
" id - a translation job ID value. You can obtain this value from the BatchTranslation example.\n";
if (args.length != 1) {
System.out.println(USAGE);
System.exit(1);
}
String id = args[0];
Region region = Region.US_WEST_2;
TranslateClient translateClient = TranslateClient.builder()
.region(region)
.build();
describeTextTranslationJob(translateClient, id);
translateClient.close();
}
// snippet-start:[translate.java2._describe_jobs.main]
public static void describeTextTranslationJob(TranslateClient translateClient, String id) {
try {
DescribeTextTranslationJobRequest textTranslationJobRequest = DescribeTextTranslationJobRequest.builder()
.jobId(id)
.build();
DescribeTextTranslationJobResponse jobResponse = translateClient.describeTextTranslationJob(textTranslationJobRequest);
System.out.println("The job status is "+jobResponse.textTranslationJobProperties().jobStatus());
System.out.println("The source language is "+jobResponse.textTranslationJobProperties().sourceLanguageCode());
System.out.println("The target language is "+jobResponse.textTranslationJobProperties().targetLanguageCodes());
} catch (TranslateException e) {
System.err.println(e.getMessage());
System.exit(1);
}
// snippet-end:[translate.java2._describe_jobs.main]
}
}
To see all the data you can get back using this code, see this JavaDoc - https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/translate/model/TextTranslationJobProperties.html

Unable to use http connector

I'm trying to use the http connector that is provided with the standard Camunda implementation with no luck. Every single time that I run my workflow the instance simply freeze on that activity. I'm using this class in an execution listnener and the code that I'm using is this:
import org.apache.ibatis.logging.LogFactory;
import org.camunda.bpm.engine.delegate.DelegateExecution;
import org.camunda.bpm.engine.delegate.Expression;
import org.camunda.bpm.engine.delegate.JavaDelegate;
import org.camunda.bpm.engine.impl.util.json.JSONObject;
import org.camunda.connect.Connectors;
import org.camunda.connect.ConnectorException;
import org.camunda.connect.httpclient.HttpConnector;
import org.camunda.connect.httpclient.HttpResponse;
import org.camunda.connect.httpclient.impl.HttpConnectorImpl;
import org.camunda.connect.impl.DebugRequestInterceptor;
public class APIAudit implements JavaDelegate {
static {
LogFactory.useSlf4jLogging(); // MyBatis
}
private static final java.util.logging.Logger LOGGER = java.util.logging.Logger.getLogger(Thread.currentThread().getStackTrace()[0].getClassName());
private Expression tokenField;
private Expression apiServerField;
private Expression questionIDField;
private Expression subjectField;
private Expression bodyField;
public void execute(DelegateExecution arg0) throws Exception {
String tokenValue = (String) tokenField.getValue(arg0);
String apiServerValue = (String) apiServerField.getValue(arg0);
String questionIDValue = (String) questionIDField.getValue(arg0);
String subjectValue = (String) subjectField.getValue(arg0);
String bodyValue = (String) bodyField.getValue(arg0);
if (apiServerValue != null) {
String url = "http://" + apiServerValue + "/v1.0/announcement";
LOGGER.info("token: " + tokenValue);
LOGGER.info("apiServer: " + apiServerValue);
LOGGER.info("questionID: " + questionIDValue);
LOGGER.info("subject: " + subjectValue);
LOGGER.info("body: " + bodyValue);
LOGGER.info("url: " + url);
JSONObject jsonBody = new JSONObject();
jsonBody.put("access_token", tokenValue);
jsonBody.put("source", "SYSTEM");
jsonBody.put("target", "AUDIT");
jsonBody.put("tType", "system");
jsonBody.put("aType", "auditLog");
jsonBody.put("affectedItem", questionIDValue);
jsonBody.put("subject", subjectValue);
jsonBody.put("body", bodyValue);
jsonBody.put("language", "EN");
try {
LOGGER.info("Generating connection");
HttpConnector http = Connectors.getConnector(HttpConnector.ID);
LOGGER.info(http.toString());
DebugRequestInterceptor interceptor = new DebugRequestInterceptor(false);
http.addRequestInterceptor(interceptor);
LOGGER.info("JSON Body: " + jsonBody.toString());
HttpResponse response = http.createRequest()
.post()
.url(url)
.contentType("application/json")
.payload(jsonBody.toString())
.execute();
Integer responseCode = response.getStatusCode();
String responseBody = response.getResponse();
response.close();
LOGGER.info("[" + responseCode + "]: " + responseBody);
} catch (ConnectorException e) {
LOGGER.severe(e.getMessage());
}
} else {
LOGGER.info("No APISERVER provided");
}
LOGGER.info("Exiting");
}
}
I'm sure that the fields injection works correctly since the class prints the correct values. I also used the http-connector in javascript in the same activity with no problem.
I'm using this approach since I need to make two different calls to external REST services in the same task, so any advice will be very welcome.
You need to enable Connect process engine plugin in process engine configuration. Not sure how you configured the process engine, make sure to add this plugin org.camunda.connect.plugin.impl.ConnectProcessEnginePlugin
Also check the following in dependencies
Do not add both dependencies - connectors-all and http-connector.
Make sure to check the error logs and see whether you have any class loading problem related to httpclient classes
I am pretty sure there is a class loading issue with http client library. make sure to include the correct version of connectors-all dependency

How to simulate a CRM plugin sandbox isolation mode in unit tests?

Context
I would like to write some unit tests against classes what will be utilized by CRM 2016 CodeActivity and Plugin classes. The final assembly will be registered in sandbox isolation mode.
I want to be sure if a test case is green when running unit tests, it will not be more restricted in sandbox isolation security restrictions when registered and run in CRM.
Question
Is there any way to simulate the sandbox isolation when running unit tests?
That's a really good question. You can maybe simulate running the plugin assemblies and code activities in a sandbox based on this Sandbox example.
With that example you could run the codeactivity with a limited set of permissions.
Now, what are the exact limitations of CRM online? Found this article. There is a Sandbox Limitations sections with some of them. If you find another one please let me know. Cause I'd be keen on adding this feature to FakeXrmEasy
Cheers,
I found this today: https://github.com/carltoncolter/DynamicsPlugin/blob/master/DynamicsPlugin.Tests/PluginContainer.cs
Which I used to turn into this:
using System;
using System.Diagnostics;
using System.Globalization;
using System.Net;
using System.Net.NetworkInformation;
using System.Reflection;
using System.Security;
using System.Security.Permissions;
using System.Text.RegularExpressions;
namespace Core.DLaB.Xrm.Tests.Sandbox
{
public static class SandboxWrapper
{
public static T Instantiate<T>(object[] constructorArguments = null)
{
return new SandboxWrapper<T>().Instantiate(constructorArguments);
}
public static T InstantiatePlugin<T>(string unsecureConfig = null, string secureConfig = null)
{
object[] args = null;
if (secureConfig == null)
{
if (unsecureConfig != null)
{
args = new object[] {unsecureConfig};
}
}
else
{
args = new object[]{unsecureConfig, secureConfig};
}
return new SandboxWrapper<T>().Instantiate(args);
}
}
public class SandboxWrapper<T> : MarshalByRefObject, IDisposable
{
private const string DomainSuffix = "Sandbox";
/// <summary>
/// The Sandbox AppDomain to execute the plugin
/// </summary>
public AppDomain SandboxedAppDomain { get; private set; }
public T Instantiate(object[] constructorArguments = null)
{
/*
* Sandboxed plug-ins and custom workflow activities can access the network through the HTTP and HTTPS protocols. This capability provides
support for accessing popular web resources like social sites, news feeds, web services, and more. The following web access restrictions
apply to this sandbox capability.
* Only the HTTP and HTTPS protocols are allowed.
* Access to localhost (loopback) is not permitted.
* IP addresses cannot be used. You must use a named web address that requires DNS name resolution.
* Anonymous authentication is supported and recommended. There is no provision for prompting the
on user for credentials or saving those credentials.
*/
constructorArguments = constructorArguments ?? new object[] { };
var type = typeof(T);
var source = type.Assembly.Location;
var sourceAssembly = Assembly.UnsafeLoadFrom(source);
var setup = new AppDomainSetup
{
ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
ApplicationName = $"{sourceAssembly.GetName().Name}{DomainSuffix}",
DisallowBindingRedirects = true,
DisallowCodeDownload = true,
DisallowPublisherPolicy = true
};
var ps = new PermissionSet(PermissionState.None);
ps.AddPermission(new SecurityPermission(SecurityPermissionFlag.SerializationFormatter));
ps.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
ps.AddPermission(new FileIOPermission(PermissionState.None));
ps.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.RestrictedMemberAccess));
//RegEx pattern taken from: https://msdn.microsoft.com/en-us/library/gg334752.aspx
ps.AddPermission(new WebPermission(NetworkAccess.Connect,
new Regex(
#"^http[s]?://(?!((localhost[:/])|(\[.*\])|([0-9]+[:/])|(0x[0-9a-f]+[:/])|(((([0-9]+)|(0x[0-9A-F]+))\.){3}(([0-9]+)|(0x[0-9A-F]+))[:/]))).+")));
// We don't need to add these, but it is important to note that there is no access to the following
ps.AddPermission(new NetworkInformationPermission(NetworkInformationAccess.None));
ps.AddPermission(new EnvironmentPermission(PermissionState.None));
ps.AddPermission(new RegistryPermission(PermissionState.None));
ps.AddPermission(new EventLogPermission(PermissionState.None));
SandboxedAppDomain = AppDomain.CreateDomain(DomainSuffix, null, setup, ps, null);
return Create(constructorArguments);
}
private T Create(object[] constructorArguments)
{
var type = typeof(T);
return (T)Activator.CreateInstanceFrom(
SandboxedAppDomain,
type.Assembly.ManifestModule.FullyQualifiedName,
// ReSharper disable once AssignNullToNotNullAttribute
type.FullName, false, BindingFlags.CreateInstance,
null, constructorArguments,
CultureInfo.CurrentCulture, null
).Unwrap();
}
#region IDisposable Support
//Implementing IDisposable Pattern: https://learn.microsoft.com/en-us/dotnet/standard/design-guidelines/dispose-pattern
private bool _disposed; // To detect redundant calls
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
if (SandboxedAppDomain != null)
{
AppDomain.Unload(SandboxedAppDomain);
SandboxedAppDomain = null;
}
}
_disposed = true;
}
// This code added to correctly implement the disposable pattern.
void IDisposable.Dispose()
{
// Do not change this code. Put cleanup code in Dispose(bool disposing) above.
Dispose(true);
}
#endregion
}
}
Which can be used as such:
SandboxWrapper.InstantiatePlugin<YourPluginType>(unsecureString, secureString)
Not sure how much of it is valid or not, but it worked for handling my testing of xml and JSON serialization correctly.

Server architecture and SubSonic

Im trying to develop an server /client application. The server will be a bunch of webservices, the idea was to expose methods like:
Company GetNewCompany(); //Creates an new Company Object
Save(Company C);
CompanyCollection GetCompany(Query q);
Where Query object is part of Subsonic 2.1. But the problem is that SubSonic is not built for this, Have I missed something here? or is it just impossible to to use subsonic query language over SOAP?
This would have been great feature, becuase then it is really easy to make an application server using subsonic.
Br
Soren.
If you want to use subsonic v3 you can look at this issue that talks about IUpdatable:
http://code.google.com/p/subsonicthree/issues/detail?id=30
This will let you use ado data services somewhat painlessly. You use a DB constructor that take a URI argument. This probably won't be a part of v3 but you could make changes like this yourself.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using WcfClientTest.NorthwindService;
namespace WcfClientTest
{
/// <summary>
/// Summary description for WcfTest
/// To run these tests, load this project, and somehow get a server running at the URI.
/// This can be done by updating the service reference to start the development server.
/// </summary>
[TestClass]
public class WcfTest
{
private string baseURI = "http://127.0.0.1:49649/Northwind.svc";
private DB ctx;
/// <summary>
/// Sets up test.
/// </summary>
[TestInitialize]
public void SetUp()
{
ctx = new DB(new Uri(baseURI));
}
[TestCleanup]
public void Cleanup()
{
}
[TestMethod]
public void Select_Simple_With_Variable()
{
int categoryID = 5;
IQueryable<Product> result = from p in ctx.Products
where p.CategoryID == categoryID
select p;
List<Product> products = result.ToList();
Assert.AreEqual(7, products.Count());
}
[TestMethod]
public void TestAddNew()
{
// add customer
var c = new Customer
{
CustomerID = "XXXXX",
ContactTitle = "Prez",
Country = "USA",
ContactName = "Big Guy",
CompanyName = "Big Guy Company"
};
ctx.AddToCustomers(c);
ctx.SaveChanges();
IQueryable<Customer> qCustomer = from cust in ctx.Customers
where cust.CustomerID == "XXXXX"
select cust;
Customer c2 = qCustomer.FirstOrDefault();
Assert.AreEqual("XXXXX", c2.CustomerID);
if (c2 != null)
{
ctx.DeleteObject(c2);
}
ctx.SaveChanges();
IQueryable<Customer> qCustomer2 = from cust in ctx.Customers
where cust.ContactName == "Big Guy"
select cust;
// Returns null if the row isn't found.
Customer c3 = qCustomer2.SingleOrDefault();
Assert.AreEqual(null, c3);
}
}
}
And this is all there is to the service:
using System.Data.Services;
using Northwind;
namespace NorthwindService
{
[System.ServiceModel.ServiceBehavior(IncludeExceptionDetailInFaults=false)]
public class Northwind: DataService<DB>
{
// This method is called only once to initialize service-wide policies.
public static void InitializeService(IDataServiceConfiguration config)
{
config.SetEntitySetAccessRule("*", EntitySetRights.All);
config.UseVerboseErrors = true;
}
}
}
And for web.config:
<system.serviceModel>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"/>
</system.serviceModel>