how can we get scenario name in pytest-bdd with pytest_bdd_before_scenario - pytest-bdd

unable to find scenario name
#sanity
Scenario Outline: B) Change User Name and validate message Master Credentials
Given the case is "<case>"
output:
scenario : B) Change User Name and validate message Master Credentials

def pytest_bdd_before_scenario(request, scenario):
print(f'SCENARIO: {str(scenario.name)}')
same as for pytest_bdd_after_scenario
def pytest_bdd_after_scenario(request, scenario):
print(f'SCENARIO: {str(scenario.name)}')

Related

How can mock a Keycloak token for unit test in ScalaTest

The app I'm working (in Scala) keep a register of all the users that login using Keycloak, for that creates a user for every new keycloak user using the information on the keycloak authorization token
private def decodeToken(token: String): Try[User] = {
Try { AdapterTokenVerifier.verifyToken(token, keycloakDeployment) } match {
case Success(accessToken: AccessToken) =>
Try (User(name = accessToken.getName, userName = accessToken.getPreferredUsername, email = Some(accessToken.getEmail))
case Failure(ex) =>
throw new Exception("authentication failed")
}}
My problem is the following, I need to do unit tests to my code, for that is used ScalaTest, how can a mock a token of keycloak inside the test so this code can be tested.
I already write the test and works but I have to pass manually a token, that means, any other time I run the test if the token expire the test files, also files if the test is run in another computer, also fails in gitlab (don't pass pipelines)
Note: I'm new with scala and with keycloak, also English is not my firs language, so it's understandable that some things are not very clear, if you think you can help me, feel free to ask anything
Note 2: With that user that is created with the Keycloak token, other things are done but this is the most relevant to my problem

Pytest-bdd giving an name error for variables defined in scenario outline

I am trying to run a test with ptest-bdd. I've used the scenario outline but the variable username is returning a name error saying that username is not defined. I would greatly appreciate a second pair of eyes on my code.
Here is the feature file:
#login
Feature: Valid Login
As a employee,
I want to login to my account
So I can access the application
Background:
Given the login page is displayed
Scenario Outline: Manager enters correct login credentials and Ok button
When the user enters their <username> and <password> correctly
And the user clicks OK
Then the page redirects to the manager home page
Examples: Manager
| username | password |
| fake_name | 1234 |
And here is the test_login_steps.py code:
from pytest_bdd import scenarios, scenario, given, when, then, parsers
from pages.signin import QaSigninPage
from pages.managerHomepage import QaManagerHomepage
import pytest
CONVERTERS = {
'username': str,
'password': str,
}
scenarios('../features/login.feature', example_converters=CONVERTERS)
# Create Page Objects
#pytest.fixture(scope='session')
def pages(test_manager):
pages={"signin_PAGE": QaSigninPage(test_manager.browser),
"manager_home_PAGE": QaManagerHomepage(test_manager.browser)}
return pages
# Given Steps
#given('the login page is displayed')
def login_visit(pages):
pages["signin_PAGE"].load()
#when('the user enters their <username> and <password> correctly')
def enter_credentials(pages):
pages["signin_PAGE"].enter_username(username)
pages["signin_PAGE"].enter_password(password)
I forgot to pass the variables into the function... I figured it out.
When you are using CONVERTERS use scenario in place of scenarios, which allows you to pass examples as parameters

Header-based authentication for REST API in Phoenix

I'm implementing a rest api in Elixir. An api-key is passed to each request to authenticate a user. In a plug I have this:
defmodule MyApp.ApiSecurity do
def init(options) do
options
end
def call(conn, _opts) do
# checking if "api-key" headers exists
# and key is valid
# .... what's next?
# if it's a) valid
# b) invalid or there's no "api-key" header
# ???
end
end
I know how to implement it for a normal, form-based authentication with a state and session. But in rest api there's no session. Then, what should I do? In other words, What should be in the rest of the function "call" when a) an api-key is valid b) invalid?
If the key is invalid or not present, you'd normally send the error message with a proper error status code and then call Plug.Conn.halt/1, which will stop this request from going further through the plug pipeline. If they key is valid, you'd probably want to assign some value to the conn, (e.g. user_id) which the rest of your application can use.
For example:
def call(conn, _opts) do
case authenticate(conn) do
{:ok, user_id} ->
conn
|> assign(:user_id, user_id)
:error ->
conn
|> send_resp(401, "Unauthenticated")
|> halt()
end
end
end
Now, any plugs that are plugged after this one can be sure that there exists a valid user_id in conn.assigns and can make use of it.
For a more real-world approach, you can see how guardian does this:
Guardian.Plug.EnsureResource
Guardian.Plug.ErrorHandler

Integrate SecureSocial with backend user services/storages?

Using Play! 2.0.4 and SecureSocial 2 (http://securesocial.ws/). Scala implementation. Most of this question will be directly referencing the sample here: https://github.com/jaliss/securesocial/blob/2.0.12/samples/scala/demo/app/service/InMemoryUserService.scala
I'm trying to figure out the author's original intention as to backend interaction with a storage service. With respect to the def find(id: UserId) and the def findByEmailAndProvider(email: String, providerId: String): methods, is SecureSocial expecting to give either a Facebook ID or email that can be used to return a full SocialUser class?
If that's the case, then how do we assign our own IDs to each user so that we can link accounts together? Because it seems that if I extend Identity to include a universal ID then would that also require rewriting/extending the social providers, too?
At minimum, I'm trying to figure out what API/parameters I should expose for the find and save methods in a backend service. Let me know if this question needs to be clarified :)
After having a couple days to make some design considerations and better understand SecureSocial, I realized that implementing the find and save methods were not that difficult to understand. It's properly designing the logic in a backend service that matters.
Basically, I created a PlatformUser class that extends the Identity class and includes User ID and profile data pulled from a backend class. Here's how it looks:
case class PlatformUser(
guid: String,
suspended: Boolean,
id: UserId,
firstName: String,
lastName: String,
fullName: String,
email: Option[String],
avatarUrl: Option[String],
authMethod: AuthenticationMethod,
oAuth1Info: Option[OAuth1Info] = None,
oAuth2Info: Option[OAuth2Info] = None,
passwordInfo: Option[PasswordInfo] = None,
communityProfile: Option[String] = None
) extends Identity
My object PlatformUser contains code that accesses a backend HTTP API to transfer data back and forth. Here's how I implement the find and save methods:
def find(id: UserId): Option[PlatformUser] = {
PlatformUser.fetch(id)
}
def findByEmailAndProvider(email: String, providerId: String): Option[PlatformUser] = {
PlatformUser.fetch(email, providerId)
}
def save(user: Identity): PlatformUser = {
PlatformUser.store(user)
}
The logic for merging accounts remains in the backend service as well. Now if the user doesn't already exist, the backend service generates a platform ID. If an email of an incoming Identity is found to already exist on the platform, then an auto-link of identities is performed to the existing platform ID (unless its found that the email is being used on multiple accounts for the same social network, where an error will be triggered). The user is notified by email to their primary address of the auto-link.
The last thing left is populating the communityProfile. If the backend service doesn't find one, then that field returns as None. I then automatically redirect the user to a "registration" page where they need to complete their profile.
That's about it. I hope this helps future devs who are trying to figure out more complicated uses of SecureSocial.
"If an email of an incoming Identity is found to already exist on the platform, then an auto-link of identities is performed to the existing platform ID". I am assuming when you say auto-link, this would be during sign on. If so, this would be a security flaw.
A malicious user could set his twitter email to your mail id. When he logs in using twitter, it gets "auto-linked" to your account!
See this thread for further analysis https://github.com/jaliss/securesocial/issues/14

How can I combine 2 validators together in play2?

For example, I want a input should be nonEmptyText and email.
If the user doesn't input anything, it will show error message The field is required, if user inputs an invalid email, it will show Invalid email.
But I found, I can just use one:
val loginForm = Form("email"->nonEmptyText)
or:
val loginForm = Form("email"->email)
For the later one, if user doesn't input, it will still show Invalid email, which is not what I want.
UPDATE1
I tried Julien's answer:
val loginForm = Form("email" -> (email verifying nonEmpty))
When user inputs nothing, the error message will be:
Valid email required, This field is required
Looks like play combines two error messages of email and nonEmpty together, but this is not exactly what I want. I hope when user not input, the message is only:
This field is required
Not:
Valid email required, This field is required
UPDATE2
I found the reason why it display combined errors is in the helper.twitterBootstrapFieldConstructor.scala.html, there is:
<span class="help-inline">#elements.errors(elements.lang).mkString(",")</span>
It combines all the errors with ,.
For my case, it should be:
<span class="help-inline">#elements.errors(elements.lang).lastOption</span>
Here, lastOption is for nonEmpty.
But I think headOption is better, but:
val loginForm = Form("email" -> (nonEmptyText verifying email))
Doesn't work -- there is no constraint of email, so we have to defined one for myself?
According to the Form documentation, you can add constraints to a mapping using the verifying method. There are a few constraints already defined in the Constraints object. So you can write:
val loginForm = Form("email" -> (email verifying nonEmpty))