-
Notifications
You must be signed in to change notification settings - Fork 0
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Enable Retrieval of Existing Users based on userid #44
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left some comments about removing preconditioned known userIds and the ways in which our intgeration tests will have to change as a result. Additionally, where we have integration tests that require a user to exist prior to their execution, it may make sense to put them in a separate .feature
file and have them all utilise a common Background step that creates a new user before all scenarios are executed. See here: https://github.com/cucumber/cucumber-js/blob/master/features/background.feature#L12
Given our user ids need to be unique (the way we've setup our mongo repository object should enforce this? Please correct me if I'm wrong), we might have to think abit more about how we set that up and how we share that uniqueId (we have a world object setup for exactly this purpose) with subsequent tests that need them.
if (PRECONDITIONED_KNOWN_USER_ID.equals(userId)){ | ||
return new User(userId, true, "", "", expectedOrganisationId); | ||
} | ||
if (PRECONDITIONED_UNKNOWN_USER_ID.equals(userId) || !userToReturn.isPresent() || !returnedOrgId.equals(userToReturn.get().getBelongsToOrgWithId())) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think now that we are integrating with mongo, we should remove all the pre-conditioned known and unknown users as it could bite us later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
theres quite a few tests that depend on these being there. How do you suggest we handle those
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, you mean the unit tests or the integration tests? If integration test, the comment I made on the PR as a whole explains a possible approach. We could also try a similar approach with our unit tests. I'll also be looking into whether there are any "in memory" Mongo solutions we can plug in for unit testing.
When I make a POST to the function at '/organisations/validOrg/users' | ||
Then I should get a status code 201 | ||
And the response should be a superset of all the keys and values set from 'newUserRequest' | ||
|
||
@getUserFromOrg @createdUserInOrg | ||
Scenario: GET to /users/{userId} with valid userId returns user | ||
When I make a GET to the function at '/organisations/validOrg/users/newUser' | ||
Then I should get a status code 200 | ||
And the response should be a superset of all the keys and values set from 'newUserRequest' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem we have here now is that we have integration tests that now depend on one another running in a certain order. As scenarios will get more complex we generally want to avoid this. One way of resolving this is to add the steps for creating the above user within the feature itself so:
When I make a POST to the function at '/organisations/validOrg/users' | |
Then I should get a status code 201 | |
And the response should be a superset of all the keys and values set from 'newUserRequest' | |
@getUserFromOrg @createdUserInOrg | |
Scenario: GET to /users/{userId} with valid userId returns user | |
When I make a GET to the function at '/organisations/validOrg/users/newUser' | |
Then I should get a status code 200 | |
And the response should be a superset of all the keys and values set from 'newUserRequest' | |
When I make a POST to the function at '/organisations/validOrg/users' | |
Then I should get a status code 201 | |
And the response should be a superset of all the keys and values set from 'newUserRequest' | |
@getUserFromOrg @createdUserInOrg | |
Scenario: GET to /users/{userId} with valid userId returns user | |
Given I generate a json payload called 'newUserRequestWithSpecificId' | |
And I make a POST to the function at '/organisations/validOrg/users' | |
And the response should be a superset of all the keys and values set from 'newUserRequestWithSpecificId' | |
When I make a GET to the function at '/organisations/validOrg/users/newUser' | |
Then I should get a status code 200 | |
And the response should be a superset of all the keys and values set from 'newUserRequestWithSpecificId' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some great stuff here. Left a comment about an improvement we could make to the repository returned by the test configuration.
|
||
@getUserFromOrg @createdUserInOrg | ||
Scenario: GET to /users/{userId} with valid userId returns user | ||
Given I generate a json payload called 'newUserRequestWithSpecificId' | ||
And I make a POST to the function at '/organisations/validOrg/users' | ||
And the response should be a superset of all the keys and values set from 'newUserRequestWithSpecificId' | ||
Given I make a GET to the function at '/organisations/validOrg/users/specificId' | ||
Then I should get a status code 200 | ||
And the response should be a superset of all the keys and values set from 'newUserRequestWithSpecificId' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We spoke about refactoring this already so no additional comments.
User retrievedUser = getUserInOrg(orgId, userId); | ||
userRepo.delete(retrievedUser); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although the scope of #27 was allowing existing users to be retrieved it seems we're now doing a bigger piece of updating all user and org management functionality to integrate with the repository? Have I understood that right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just the user functionality. Once the preconditioned users were removed it was really just a 3 line change to add the other functionality(162, 166 and 167) and it was a convenient way to see that the test changes worked.
@TestConfiguration | ||
public class TestConfig { | ||
Map<String, User> createdUsers = new HashMap<>(); | ||
|
||
@Bean | ||
MathDojoUserRepository userRepo() { | ||
// configured known user | ||
createdUsers.put("aKnownUser", new User("aKnownUser", false, "", "", "aKnownOrg")); | ||
MathDojoUserRepository userRepo = new MathDojoUserRepository() { | ||
|
||
@Override | ||
public <S extends User> Optional<S> findOne(Example<S> example) { | ||
// TODO Auto-generated method stub | ||
return null; | ||
} | ||
|
||
@Override | ||
public <S extends User> Page<S> findAll(Example<S> example, Pageable pageable) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I really like this approach. I do wonder whether we could make this more flexible & easier to use. Rather than a hardened hand-written config that replicates the actual bean (we had to implement all the methods) could we return a mochito version of this? That would allow us to control behaviour in the test in which it is being used. Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It may be possible, and I would be happy to switch to a mockito version if the need for flexibility arises but I thought that a concrete reusable mock repository fit our needs better, because the behaviour should ideally be always fixed just like the actual mongo repo object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
From my understanding, using mockito wouldn't stop it from being a "concrete" implementation per se. It's more a general "code smell" if in one of your tests you end up having to implement all the methods for something that is supposed to be a mock of an implementation. It's not a hard stop for me, it's something I learned and figured was worth sharing.
import com.microsoft.azure.functions.ExecutionContext; | ||
import com.microsoft.azure.functions.HttpRequestMessage; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems we don't use these?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just organising imports with an eclipse shortcut
@@ -33,6 +32,8 @@ | |||
import io.mathdojo.useraccountservice.services.SystemService; | |||
|
|||
@RunWith(SpringRunner.class) | |||
@ContextConfiguration(classes = TestConfig.class) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯 🎉🎉🎉
@@ -33,6 +32,8 @@ | |||
import io.mathdojo.useraccountservice.services.SystemService; | |||
|
|||
@RunWith(SpringRunner.class) | |||
@ContextConfiguration(classes = TestConfig.class) | |||
@SuppressWarnings({"rawtypes","unchecked"}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does this do?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
purely cosmetic for compiler warnings. We have a good reason for not adding concrete types to some objects
Logger testLogger = mock(Logger.class); | ||
Mockito.when(targetExecutionContext.getLogger()).thenReturn(testLogger); | ||
Mockito.when(userRepo.save(Mockito.any(User.class))).thenAnswer(new Answer<User>() { | ||
public User answer(InvocationOnMock invocation) { | ||
return (User) invocation.getArguments()[0]; | ||
} | ||
}); | ||
Mockito.when(userRepo.findById(PRECONDITIONED_KNOWN_USER_ID)).thenReturn(Optional.of(new User(PRECONDITIONED_KNOWN_USER_ID, false, "", "", PRECONDITIONED_KNOWN_ORG_ID))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 🎉💯🔥🎉
Background: Create pre-configured user | ||
Given I generate a json payload called 'userWithKnownId' | ||
And I make a POST to the function at '/organisations/validOrg/users' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍🏾
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. There's a discussion point I've put on, for which I've raised this follow on issue: #48
}, | ||
}, | ||
userWithKnownId: { | ||
id: "knownUserId", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am surprised that we are not having issues here. This id parameter should be unique so it really shouldn't let us register lots of users with the same "id". Correct me if I'm wrong but I thought we specified this in the configuration with mongo somewhere?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On reflection, we couldn't have noticed this earlier because we were using pre-conditioned users for the tests. Now, we probably need to add a unit test about no duplicate users and modify the tests so that they don't keep registering the same user. Happy to do that in a separate PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The implication/likely scenario for this is that if a user does try to signup on Auth0 (maybe the forgot whether they've signed up with a certain account before), we'll end up creating a new profile etc. What they should get is either some message saying they've registered before or actually just get back their existing profile.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By creating our own id field we are implicitly taking on the burden of enforcing uniqueness. I assumed this was always planned. Mongo has a different id field that differentiates objects. I'm happy to do the other PR as well but it's obviously outside the scope here
@@ -444,7 +444,7 @@ public void throwsErrorIfAttemptToUpdateValidUserWithNullParams() { | |||
|
|||
@Test | |||
public void throwsNoErrorIfDeletingForValidUserInValidOrgId() { | |||
organisationService.deleteUserFromOrg("knownOrganisationId", "knownUserId"); | |||
organisationService.deleteUserFromOrg("aKnownOrg", "aKnownUser"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably worth using the constants you created above here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just one question about the timeout as I don't think we should use that at the stage you've put it.
When(/I make a (\w+) to the function at \'(.*)\'/, function (httpMethod, path) { | ||
|
||
When(/I make a (\w+) to the function at \'(.*)\'/,{ | ||
timeout: 100 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we have a timeout here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just one question about the timeout as I don't think we should use that at the stage you've put it.
The problem is the rate limit you set on the api gateway does not allow for the background step to work. Obviously there are many ways to solve this. It's also useful if you say why you don't think it should be there as in what harm is done by having a timeout there.
}, | ||
}, | ||
userWithKnownId: { | ||
id: "knownUserId", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By creating our own id field we are implicitly taking on the burden of enforcing uniqueness. I assumed this was always planned. Mongo has a different id field that differentiates objects. I'm happy to do the other PR as well but it's obviously outside the scope here
In terms of why a timeout in the request stage isn't the right approach: The timeout essentially says, "wait until this window has passed before failing the test if the promise hasn't resolved." Here are the docs if you're curious. Currently in the tests we have timeouts in 2 places. The first is on the startup of the app, in The second timeout is for the step where we assert the status codes of responses. That one is necessary because the Azure function when deployed has a cold start period (the container in which our code runs has been de-provisioned and so Azure needs to "wake up the app"). Given you're trying to throttle requests, the timeout won't do what you're hoping. It's easy to modify the rate limit, just log onto the API gateway and see what it is for the API in question. |
Sorry I still don't see what long or short term harm it does. The linked docs say nothing about what you mentioned. And it seemed to be a convenient way to add a short wait before we send http requests. Your proposed solution of changing the rate on the gateway doesn't seem to scale |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
:+1
pr${number}
and an override target url set up on the user-accout-test apiPurpose
Does this introduce a breaking change?
Pull Request Type
What kind of change does this Pull Request introduce?
How to Test
What to Check
Verify that the following are valid
Other Information