How to input #TestTarget point when do provider verification? - pact

I am a new learner for pact and I want to know what should I input when do provider verification
for provider verification I should fill provided target as localhost only or instead of localhost i also can input actual env's host? which scenario is the best for contract test?
public class LoginProviderTest {
private static final Logger LOGGER = LoggerFactory.getLogger(LoginProviderTest.class);
#TestTarget
public final Target target = new HttpTarget(?????);

if you are using Springboot, you can try like,
#TestTarget
public final Target target= new SpringBootHttpTarget();

Related

How to test annotation based role-specific security in test annotated with `#WebMvcTest` and `#WithMockUser`

My Problem is that I can not test the role-based authentication of my annotated Rest Endpoints in my tests. The specified roles seem to make no difference
I am using annotation based security configuration on my REST-controllers like this:
#RestController
#RequestMapping("rest/person")
class PersonRestController(
private val securityService: SecurityService,
private val personService: PersonService,
) {
#GetMapping("/list")
#Secured(UserRole.Role.ROLE_COMPANY)
fun list(): List<Person> {
val companyId = securityService.currentUser.companyId
return personService.findByCompany(companyId)
}
}
In my Web Layer tests I am using #WebMvcTest with a shared config class, that provides all required beans (we have some shared ExceptionHandlers and Interceptors that I would like to test with my Controllers)
My Test looks like this:
#WebMvcTest(PersonRestController::class)
#Import(RestControllerTestConfig::class)
class GroupedScheduleRestControllerTest {
#Autowired
private lateinit var mvc: MockMvc
#MockBean
private lateinit var personService: PersonService
// This bean is provided by RestControllerTestConfig
#Autowired
private lateinit var someSharedService: SomeSharedService
#Test
#WithMockUser(value = "user#company.at")
fun testReturnsEmptyList() {
val response = mvc.perform(MockMvcRequestBuilders.get("/rest/person/list"))
response.andExpect(status().isOk)
.andExpect(jsonPath("$.length()").value(0))
}
}
Now I would like to add a unit test, that verifies, that only a user with the role COMPANY can access this endpoint - but I can't get this to work. My test always runs through when I add WithMockUser, independent of what I pass in for the roles.
And it always fails with a 401 Unauthorized when I remove WithMockUser so some security setup seems to be happening but the #Secured in my RestEndpoint seems to have no effect.
Am I missing some configuration here to notify #WebMvcTest to pick up the Security annotations from the instantiated RestController?
Okay in order for the #Secured annotations to be picked up, I added #EnableGlobalMethodSecurity(securedEnabled = true) to my Configuration class and it worked like a charm 🙈

Provider test integration with pact broker for Spring Boot junit5 + configuration in application properties

The pact-jvm-provider-spring states that for junit5 provider test, it is not required to use the spring library.
However, #PactBroker annotation depends on the system properties. Is there a way to get this working for application properties via the Spring Property Resolver. I tried to create something similar to SpringEnvironmentResolver.kt and used it in the context setup. But that did not work.
#Provider("api-provider-app")
#PactBroker
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles("test")
public class PactVerificationTest {
#LocalServerPort
private int port;
#Autowired
private Environment environment;
#TestTemplate
#ExtendWith(PactVerificationInvocationContextProvider.class)
void testTemplate(Pact pact, Interaction interaction, HttpRequest request,
PactVerificationContext context) {
context.setTarget(new HttpTestTarget("localhost", port));
context.setValueResolver(new SpringResolver(environment));
context.verifyInteraction();
}
}
I get the following error
Invalid pact broker host specified ('${pactbroker.host:}'). Please provide a valid host or specify the system property 'pactbroker.host'.
Update
After some more searching found out that the setTarget was not working and that needs to be moved to #BeforeEach method.
#BeforeEach
void setContext(PactVerificationContext context) {
context.setValueResolver(new SpringResolver(environment));
context.setTarget(new HttpTestTarget("localhost", port));
}
The following snippet helped it work with #PactFolder annotation. But the #PactBroker with properties is still not working
There is a new module added to Pact-JVM that extends the JUnit5 support to allow values to be configured in the Spring Context. See https://github.com/DiUS/pact-jvm/tree/master/provider/pact-jvm-provider-junit5-spring. It will be released with the next version of Pact-JVM, which will be 4.0.7.

UnitTest in ASP.NET with Postgres

I write some tests of created system which worked with PostgreSQL. I create in solution new project with type Class Library (.NET Core). Then, i create class, which testing class DocumentRepository. But in constructor of DocumentRepository is used IConfiguration (for connecting with database), and this IConfiguration i can't call in test class. How i can to imitate connecting with database in UnitTest?
Here class, which i want testing
public class DocumentsRepository : IRepository<Documents>
{
private string connectionString;
public DocumentsRepository(IConfiguration configuration, string login, string password)
{
connectionString = configuration.GetValue<string>("DBInfo:ConnectionString");
connectionString = connectionString.Replace("username", login);
connectionString = connectionString.Replace("userpassword", password);
}
internal IDbConnection Connection
{
get
{
return new NpgsqlConnection(connectionString);
}
}
public void Add(Documents item)
{
using (IDbConnection dbConnection = Connection)
{
dbConnection.Open();
dbConnection.Execute("SELECT addrecuserdocuments(#DocumentName,#Contents,#DocumentIntroNumber)", item);
}
}
Here's test, which i try use
using FluentAssertions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using WebApplication4.Controllers;
using WebApplication4.Entites;
using WebApplication4.ViewModels;
using Xunit;
namespace TestsApp
{
public class UserControllerTest
{
private IConfiguration configuration;
private string connectionString;
[Fact]
public async Task IndexUsers()
{
connectionString = configuration.GetValue<string>("DBInfo:ConnectionString");
var aCon = new AccountController(configuration);
var uCon = new UserController(configuration);
LoginModel model = new LoginModel
{
Login = "postgres",
Password = "111"
};
aCon.Authorization(model);
var result = uCon.Index();
var okResult = result.Should().BeOfType<OkObjectResult>().Subject;
var persons = okResult.Value.Should().BeAssignableTo<IEnumerable<Documents>>().Subject;
persons.Count().Should().Be(7);
}
}
}
Test show my error on
var result = uCon.Index();
And get me NullReferenceException.
How i can resolve this problem?
First and foremost, you're not unit testing, you're integration testing. As soon as you've got something like a database connection in the mix, unit testing is well out the window. If your goal is to write unit tests for your repository class, you should be mocking the data store.
Second, you should not inject IConfiguration, if you need some data from your configuration, such as a connection string, you should bind it to a strongly-typed class, and inject that instead:
services.Configure<MyConnectionStringsClass>(Configuration.GetSection("ConnectionStrings"));
Then, inject IOptionsSnapshot<MyConnectionStringsClass> instead.
Third, you really shouldn't be handling it this way, anyways. If you repository has a dependency on IDbConnection, then you should be injecting that into your repository. In Startup.cs:
services.AddScoped(p => new NpgsqlConnection(Configuration.GetConnectionString("Foo"));
Then, accept NpgsqlConnection in your repo constructor and set it to a private field.
Fourth, if you insist on continuing the way you currently are, you should absolutely not have a custom getter on your Connection property that news up NpgsqlConnection. That means you'll get a new instance every single time you access this property. Instead, you should define it as simple { get; private set; }, and set it in your repo's constructor.
Fifth, you should not be using using with a property defined in either way, as it will be disposed after the first time you do it, making all subsequent queries fail with an ObjectDisposedException. If you're going to new it up in your class, then your class needs to implement IDisposable and you should dispose of your connection in the Dispose method. FWIW, if you inject all dependencies (including your connection) into your class, you don't need to implement IDisposable as there's nothing the class will own that it needs to dispose of - another great reason to use dependency injection all the way down.
Finally, to answer you main question, you should use TestServer. When creating a TestServer you pass it your Startup class as a type param, so you end up with a true replica of your actual app, with all the appropriate services and such. Then, you can issue HTTP requests, like you would with HttpClient to test your controller actions and such. However, again, this is for integration testing only, which is the only time you should actually have a PostreSQL database in-play anyways.

It is possible to retrieve host address in application service in abp framework?

It is possible to have host address in app services?
For example we want send email to customer with specific link point to site address. How is this possible?
This came up via Google & the existing answer didn't really help. I don't necessarily agree that app services are the wrong domain for this; in ABP for example, a service is closely connected to a controller and services usually only exist in order to service web requests there. They often execute code in an authorised state that requires a signed-in user, so the whole thing is happening in the implicit domain context of an HTTP request/response cycle.
Accordingly - https://learn.microsoft.com/en-us/aspnet/core/fundamentals/http-context?view=aspnetcore-2.2#use-httpcontext-from-custom-components
Add services.AddHttpContextAccessor(); to something like your Startup.cs - just after wherever you already call services.AddMvc().
Use dependency injection to get hold of an IHttpContextAccessor in your service - see below.
Using constructor-based dependency injection, we add a private instance variable to store the injected reference to the context accessor and a constructor parameter where the reference is provided. The example below has a constructor with just that one parameter, but in your code you probably already have a few in there - just add another parameter and set _httpContextAccessor inside the constructor along with whatever else you're already doing.
using HttpContext = Microsoft.AspNetCore.Http.HttpContext;
using IHttpContextAccessor = Microsoft.AspNetCore.Http.IHttpContextAccessor;
// ...
public class SomeService : ApplicationService
{
private readonly IHttpContextAccessor _httpContextAccessor;
public SomeService(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
}
Now service code can read the HTTP context and from there things like the HTTP request's host and port.
public async Task<string> SomeServiceMethod()
{
HttpContext context = _httpContextAccessor.HttpContext;
string domain = context.Request.Host.Host.ToLowerInvariant();
int? port = context.Request.Host.Port;
// ...
}
HttpContext.Current.Request.Url
can get you all the info on the URL. And can break down the url into its fragments.

Spring log request payload

I want to log every incoming request data and payload(body). How to configure that in Spring Boot? And how to hide sensitive data like password in logs?
Is it possible to log the original 'raw' request body (e.g. JSON)?
You could use AOP (Aspect Oriented programming) and intercept all the requests and log the info you need. Also you can filter which kind of requests need to log. An example with Spring-Boot could be this code
If you want to skip some methods from the logging in the aspect you can add this:
Create an annotation
#Retention(RetentionPolicy.RUNTIME)
public #interface NoLogging {}
#Aspect
#Component
public class LogsAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(EventAspect.class);
#Before("execution(* com.your.controller..*Controller.*(..)) && !#annotation(NoLogging)")
public void beforeController(JoinPoint joinPoint){
String packageName = joinPoint.getSignature().getDeclaringTypeName();
String params = getArguments(joinPoint);
String methodName = joinPoint.getSignature().getName();
//Log here your message as you prefer
}
}
And in any method from your Controllers, if you want to avoid the logging by aspects add the annotation
#RequestMapping(value = "/login", method = RequestMethod.GET)
#NoLogging
public ModelAndView loginUser(){
//actions
}
IMO, to log any incoming request should place at webserver level instead of application level.
For example, you could turn on/off access_log at Nginx.

Resources