I have a function that would keep looping until it got a correct username input. I try to write the unit test of it, but cannot figure out a proper way. The test always failed at the scan.nextLine(). Can anyone help?
Function:
User currentUser = null;
String name = null;
do {
System.out.print("Please enter your user name: ");
name = scan.nextLine();
currentUser = User.userExist(name); //if user exist return user, else return null
if (currentUser == null)
logging.log("<LOG> Non-existent user "+name+", Please enter again!");
}while(currentUser == null);
Unit tests (assume your using jUnit) are supposed to run without any human input. Your best bet is to simulate the user input by creating a mock object of the scan class and have your tests set the mock object during setup.
EG something like this (pseudo code):
// test class
#Before() {
MockScan mockscan = new MockScan();
classToTest.setScan(mockScan);
}
// mock class - should implement same interface as original
// scan object or extend it and override its methods
class MockScan implements ScanInterface {
public String nextLine() {
return "John Doe";
}
}
If the object you need to mock is complicated or provided by the system there are a few frameworks out there to help - search google.
HTH
Try like that to work scan.nextLine();
User currentUser = null;
String name = null;
Scanner scan = null
while(currentUser == null)
System.out.print("Please enter your user name: ");
scan = new Scanner(System.in); //to ask user input
name = scan.nextLine();
currentUser = User.userExist(name); //if user exist return user, else return null
if (currentUser == null)
logging.log("<LOG> Non-existent user "+name+", Please enter again!");
else
scan.close();
}
Related
Using keycloak 11 I tried to find the page for the already logged in user.
I want to customize this page but I fail to find it.
I looked into the message to find the message, and I did its called alreadyLoggedIn.
I tried looking into each template belonging to the base/login folder but I still didn't find the variable. So I supposed its set somewhere in the code but I didn't find anything in the doc or the forums.
anyone please provide the template name and/or its location in the keycloak repo ?
The page you are looking for is template.ftl. The content of alreadyLoggedIn is in h1#kc-page-title and set by a nested header.
The info.ftl page is the one being used for ALREADY_LOGGED_IN message. The template.ftl is never directly used for showing any page from what I know.
You can see from the source code
/**
* Verifies that the authentication session has not yet been converted to user session, in other words
* that the user has not yet completed authentication and logged in.
*/
public static <T extends JsonWebToken> void checkNotLoggedInYet(ActionTokenContext<T> context, AuthenticationSessionModel authSessionFromCookie, String authSessionId) throws VerificationException {
if (authSessionId == null) {
return;
}
UserSessionModel userSession = context.getSession().sessions().getUserSession(context.getRealm(), authSessionId);
boolean hasNoRequiredActions =
(userSession == null || userSession.getUser().getRequiredActionsStream().count() == 0)
&&
(authSessionFromCookie == null || authSessionFromCookie.getRequiredActions() == null || authSessionFromCookie.getRequiredActions().isEmpty());
if (userSession != null && hasNoRequiredActions) {
LoginFormsProvider loginForm = context.getSession().getProvider(LoginFormsProvider.class).setAuthenticationSession(context.getAuthenticationSession())
.setSuccess(Messages.ALREADY_LOGGED_IN);
if (context.getSession().getContext().getClient() == null) {
loginForm.setAttribute(Constants.SKIP_LINK, true);
}
throw new LoginActionsServiceException(loginForm.createInfoPage());
// ^^ createInfoPage is being called
}
}
Im attempting to create a forgot password feature in asp core with entity framework.
[UnitOfWork]
public virtual async Task PasswordReset(PasswordResetViewModel resetPasswordViewModel)
{
//var user = await GetUserByChecking(emailAddress);
var user = await _userManager.FindByEmailAsync(resetPasswordViewModel.UsernameOrEmailAddress);
if (user == null)
{
throw new UserFriendlyException("User not found!");
}
var result = await _userManager.ResetPasswordAsync(user, resetPasswordViewModel.PasswordResetToken, resetPasswordViewModel.NewPassword);
switch (result.Succeeded)
{
case true:
throw new UserFriendlyException("Password Reset");
case false:
throw new UserFriendlyException(result.Errors.ToString());
default:
break;
}
}
when the following runs
userManager.ResetPasswordAsync
i get a successful result however i am still unable to login with the new password and the old password continues to work.
I have fixed this. The issue was that I returned an exception on a successful password reset. This for some reason did not save the change . I changed it to return a JSON string and it worked
I Implementd a custom userservice to store user data in a database.
Since that I get a NullReferenceException when I try to authenticace with an external provider like facebook.
I can see this stack in the exception which indicates that a value is missing for the loginpage. As A result after clicking on the facebook button i'm standing again at the starting login page.
But I don't know why or which value exactly
I can see that at the end of AuthenticateExternal context.AuthenticateResult.User.Claims contains these claims
my user service looks simplified like this
public override async Task AuthenticateExternalAsync(ExternalAuthenticationContext context)
{
string id = context.ExternalIdentity.Claims.FirstOrDefault(i => i.Type == "id").Value;
var user = await gateway.ByExternalIds(context.ExternalIdentity.Provider, id);
if (user == null)
{
string displayName = context.ExternalIdentity.Claims.FirstOrDefault(i => i.Type.Equals("urn:facebook:name")).Value;
user = new User(context.ExternalIdentity);
await gateway.StoreAsync(user);
}
if (user != null)
{
await gateway.SetLastLogin(user.Subject, DateTimeOffset.Now);
context.AuthenticateResult = new AuthenticateResult(user.Subject, GetDisplayName(user), identityProvider: context.ExternalIdentity.Provider);
}
}
What am I missing?
Found it. This behavior occurs when the method for detemerning if the user ist active returns false.
Task IsActiveAsync(IsActiveContext context)
its little unexpected as I had expected that in this case i would see something like "unknown user" or "inactive user"
I am having trouble in validating and reseting some fields based on the role of a user.
I am trying to develop a rest api with grails and my problem appears when i try to reset some fields based on the role of an user. I send a json with the desired "not allowed" changes via PUT to the controller. I modify the not allowed fields to ones that are correct for me and then call .save() and the "not alowed" fields are updated with their sent value, not with the modified by me values. Here is the code.
THE MODEL
package phonebook
class User {
String firstName
String lastName
String phoneNo
String address
String email
String password
boolean active = false
String hash
String authToken = ""
String role = "user"
static hasMany = [contacts:Contact]
static constraints = {
firstName(blank: false)
lastName(blank: false)
address(blank: true)
phoneNo(unique: true)
email(blank: false, unique: true)
password(blank: false)
role(blank: false, inList: ["user", "admin"])
hash(blank: true)
authToken(blank: true)
active(inList:[true,false])
}
}
THE METHOD FROM CONTROLLER:
#Transactional
def update(User userInstance) {
if (!isAuthenticated()){
notAllowed()
return
}
if (userInstance == null) {
notFound()
return
}
//if(isAdmin()){
def userBackup = User.findById(userInstance.id)
userInstance.role = userBackup.role
userInstance.active = userBackup.active
userInstance.hash = userBackup.hash
userInstance.authToken = userBackup.authToken
//}
if (userInstance.hasErrors()) {
respond userInstance.errors, view:'edit'
return
}
userInstance.save flush:false
request.withFormat {
'*'{ respond userInstance, [status: OK] }
}
}
THE JSON SENT VIA PUT
{
"id":"1",
"firstName": "Modified Name 23",
"role":"admin",
"active":"true",
"hash":"asdasd"
}
The above code should not modify my values for hash or active or role even if they are sent.
Any ideas?
Thanks.
The reason your changes are being saved is because by default any changes made to a domain instance will be flushed at the end of the session. This is known as open session in view with automatic session flushing. I recommend you do some reading on some of the main issues people face with GORM.
Proper use of discard may solve your issue. Discard your instance changes before you exit your controller.
For example:
if (!isAuthenticated()){
notAllowed()
userInstance.discard()
return
}
Edit
Based on conversation in the comments this perhaps may be the way to address your issue. A combination of discard and attach.
userInstance.discard()
def userBackup = User.findById(userInstance.id)
userInstance.role = userBackup.role
userInstance.active = userBackup.active
userInstance.hash = userBackup.hash
userInstance.authToken = userBackup.authToken
userInstance.attach()
I was helped by this method.
getPersistentValue
Example
def update(ShopItem shopItemInstance) {
if (shopItemInstance == null) {
notFound()
return
}
if (!shopItemInstance.itemPhoto){
shopItemInstance.itemPhoto =
shopItemInstance.getPersistentValue("itemPhoto");
}
if (shopItemInstance.hasErrors()) {
respond shopItemInstance.errors, view:'edit'
return
}
shopItemInstance.save flush:true
redirect(action: "show", id: shopItemInstance.id)
}
In your case:
userInstance.role = userInstance.getPersistentValue("role")
userInstance.active = userInstance.getPersistentValue("active")
userInstance.hash = userInstance.getPersistentValue("hash")
userInstance.authToken = userInstance.getPersistentValue("authToken")
It's better if you'll use the command objects feature. You can bind a command object with the request payload, validate it and than find and update the domain object.
You can find more details here:
http://grails.org/doc/2.3.x/guide/theWebLayer.html#commandObjects
And off the record you shoudn't use #Transactional in your controller. You can move that code into a service.
Eq:
def update(Long id, UserCommand cmd){
// Grails will map the json object into the command object and will call the validate() method if the class is annotated with #Validatable
}
I am using Specflow, nunit and moq to test the default MVC2 application registration as I learn SpecFlow.
I have the following steps for checking if the username and password have not been entered.
Steps
[Given(#"The user has not entered the username")]
public void GivenTheUserHasNotEnteredTheUsername()
{
_registerModel = new RegisterModel
{
UserName = null,
Email = "test#dummy.com",
Password = "test123",
ConfirmPassword = "test123"
};
}
[Given(#"The user has not entered the password")]
public void GivenTheUserHasNotEnteredThePassword()
{
_registerModel = new RegisterModel
{
UserName = "user" + new Random(1000).NextDouble().ToString(),
Email = "test#dummy.com",
Password = string.Empty,
ConfirmPassword = "test123"
};
}
[When(#"He Clicks on Register button")]
public void WhenHeClicksOnRegisterButton ()
{
_controller.ValidateModel(_registerModel);
_result = _controller.Register(_registerModel);
}
[Then(#"He should be shown the error message ""(.*)"" ""(.*)""")]
public void ThenHeShouldBeShownTheErrorMessage(string errorMessage, string field)
{
Assert.IsInstanceOf<ViewResult>(_result);
var view = _result as ViewResult;
Assert.IsNotNull(view);
Assert.IsFalse(_controller.ModelState.IsValid);
Assert.IsFalse(view.ViewData.ModelState.IsValidField(field));
Assert.IsTrue(_controller.ViewData.ModelState.ContainsKey(field));
Assert.AreEqual(errorMessage,
_controller.ModelState[field].Errors[0].ErrorMessage);
}
Extension method to force validation
public static class Extensions
{
public static void ValidateModel<T> ( this Controller controller, T modelObject )
{
if (controller.ControllerContext == null)
controller.ControllerContext = new ControllerContext();
Type type = controller.GetType();
MethodInfo tryValidateModelMethod =
type.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance).Where(
mi => mi.Name == "TryValidateModel" && mi.GetParameters().Count() == 1).First();
tryValidateModelMethod.Invoke(controller, new object[] { modelObject });
}
}`
I do not understand why the password missing test fails on the following lines.
Assert.IsFalse(view.ViewData.ModelState.IsValidField(field));
Assert.IsTrue(_controller.ViewData.ModelState.ContainsKey(field));
I have noticed that the error message being returned is for the Password and ConfirmPassword not matching but I dont understand why for all the other tests, including the Missing Confirm Password test (Identical to the missing Password test) they work fine.
Any ideas?
Features
Scenario: Register should return error if username is missing
Given The user has not entered the username
When He Clicks on Register button
Then He should be shown the error
message "The Username field is required." "username"
Scenario: Register should return error if password is missing
Given The user has not entered the
password
When He Clicks on Register button
Then He should be shown the error message "'Password' must be at least
6 characters long." "Password"
UPDATE
Ok seems the ValidatePasswordLengthAttribute in the Account Model couldn't initilise Membership.Provider as I did not have the connectionstring in my app.config. Is the Pembership.Provider connecting to the membership DB now?
I have added the connection string but now the test passes 50% of the time as it returns two errors:
Password required
Password must be 6 chars long.
The problem is that they are not returned in the same order every time so the test is flaky.
How can I rewrite my scenario and test to account for this? Can I still keep the one "Then" method or do I need to create a new method?
Thanks.
I had to add the connection string the the AccountService to the App.config which nunit uses. This was causing an error on the ValidatePasswordLengthAttribure.
I have updated the Assert which checks for the correct error message to:
Assert.AreEqual(errorMessage,
_controller.ModelState[field].Errors.First(e => e.ErrorMessage == errorMessage).ErrorMessage);
Still unsure about whether the Membership.Provider is hitting the DB