I would like to have a AuthWrapper Service that wraps the AngularFireAuth Service. Something like this.
// EDIT: Adding some import statements.
import {TestBed} from '#angular/core/testing';
import { AuthWrapperService } from './auth-wrapper.service';
import {AngularFireModule} from '#angular/fire';
import {AngularFireAuth, AngularFireAuthModule} from '#angular/fire/auth';
import {environment} from '../environments/environment';
#Injectable({
providedIn: 'root'
})
export class AuthWrapper {
constructor(public afAuth: AngularFireAuth) { }
isAuthenticated(): Observable<firebase.User> {
return this.afAuth.user;
}
createUserWithEmailAndPassword(email: string, password: string): Promise<String> {
let authPromise: Promise<firebase.auth.UserCredential> =
this.afAuth.auth.createUserWithEmailAndPassword(email, password);
return authPromise
.then((value) => {
return value.user.email;
})
.catch(function(error) {
return error.message;
});
}
}
I want a wrapper, so that I can test my connection to AngularFireAuth. Other tests mock the AuthWrapper.
( Reason for not mocking AngularFireAuth: Say I mock AngularFireAuth, then I am determining the mock's return values. Then I am saying that I understand what these values would look like. It is not safe to assume this without ever testing these by calling the backend. Say google changes how the results of the real AngularFireAuth's methods, I would then be forced to change the results of each of my AngularFireAuth mocks. Instead it is better to wrap AngularFireAuth in a wrapper, and just change that wrapper's methods to conform to google's changes. )
My tests in Karmine and Jasmine result in an "Async callback was not invoked within 5000ms error." I know the user is signed it because the first expect passes, but how do I get the second expect to work?:
describe('AuthWrapperService', () => {
let fireAuthService: AngularFireAuth;
let password = "dcbA4321!";
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
AngularFireModule.initializeApp(environment.firebase),
AngularFireModule,
AngularFireAuthModule
],
providers: [AngularFireAuth, AuthWrapperService],
declarations: []
})
fireAuthService = TestBed.get(AngularFireAuth);
});
it('should sign in user', async() => {
const email = "anyemail#gmail.com";
let authWrap = new AuthWrapperService(fireAuthService);
let userEmail = await authWrap.createUserWithEmailAndPassword(email, password);
expect(userEmail).toBe("anyemail#gmail.com");
let obs = authWrap.isAuthenticated();
let promise = obs.toPromise();
let user = await promise.then((result) => {return result});
expect(user.email).toBe("anyemail#gmail.com");
});
});
I haven't seen any Angular testing code, where the AngularFireAuth isn't mocked.
These are the tests that worked for me with a time interval of 5000ms. I did test them with "bad#gmail.com" in the expect lines to make sure the expect lines were actually running, and yes, the expect lines are actually passing. I intend to only run the AuthWrapperService tests periodically since they hit the database. I will mock the AuthWrapperService in other tests. Before I run these tests I delete the existing users in the firebase database manually through the firebase console. I do run both of these tests in one go, (inside one describe block).
it('should create new user', async() => {
const annEmail = "ann#gmail.com";
let authWrap = new AuthWrapperService(fireAuthService);
await authWrap.createUserWithEmailAndPassword(annEmail, password).then(
((testEmail) => {
expect(fireAuthService.auth.currentUser.email).toBe(annEmail);
})
)
await fireAuthService.auth.signOut();
});
it('should return error message when user already exists', async() => {
const email = 'ben#gmail.com';
const error = 'already in use';
let authWrap = new AuthWrapperService(fireAuthService);
await authWrap.createUserWithEmailAndPassword(email, password)
.then((value: String) => {
expect(fireAuthService.auth.currentUser.email).toBe(email);
});
await fireAuthService.auth.signOut();
expect(fireAuthService.auth.currentUser).toBeNull();
await authWrap.createUserWithEmailAndPassword(email, password)
.then((value: String) => {
expect(value).toContain('already in use');
});
await fireAuthService.auth.signOut();
});
I decided to use fireAuthService.auth.currentUser.email instead of the AuthWrapperService's isAuthenticated() method to directly test that my AuthWrapperService's createUserWithEmailAndPassword() method creates a user in firebase.
I realize that this is not a unit test, but I have decided that it is better to have an integration test than to mock methods that I don't own.
The following test also worked for me within 5000ms and uses the isAuthenticated() method.
it('should create user cat', async() => {
const catEmail = "cat#gmail.com";
let authWrap = new AuthWrapperService(fireAuthService);
let userEmail = await authWrap.createUserWithEmailAndPassword(catEmail, password);
expect(userEmail).toBe(catEmail);
await authWrap.isAuthenticated().subscribe({
next(user) {
expect(user.email).toBe(catEmail);
}
});
console.log("cat, timeinterval: " + jasmine.DEFAULT_TIMEOUT_INTERVAL);
await fireAuthService.auth.signOut();
});
The following "dan test" resulted in time outs. I tried 29000ms once and it ran out of time. Most runs of this test were 10 seconds. Many times, but not always, this test passes correctly if the user is already a user before the test is run (so it's not really creating a user during the test). One time this test passed correctly when the user had not been created before the test, but I ran this many times without the user existing before the test and it failed. I'm not sure why that would be.
it('should create user dan', done => {
const datEmail = "dan#gmail.com";
let authWrap = new AuthWrapperService(fireAuthService);
authWrap.isAuthenticated().subscribe(
{
next(user){
expect(user.email).toBe(datEmail);
done();
}
}
);
authWrap.createUserWithEmailAndPassword(datEmail, password);
console.log("dan, timeinterval: " + jasmine.DEFAULT_TIMEOUT_INTERVAL);
});
As discussed in comments, its not a good practice to not mock dependencies in case of unit testing. Any cohesive unit should be tested in isolation. And let say if our dependency has number of other dependencies then we cannot provide them all.
When you test the code with the actual dependency, you are not doing unit testing; you are doing integration testing.
Please see this for difference between Integration testing and Unit testing - https://angular.io/guide/testing#use-e2e-end-to-end-to-test-more-than-a-single-unit
You are facing this error because your async spec finishes after the default time out that jasmine has specified, which is 5 seconds. Please find the jasmine documentation on this. URL - https://jasmine.github.io/2.0/introduction.html#section-42
You need to manipulate the jasmine.DEFAULT_TIMEOUT_INTERVAL as per your need, assign it some large value as shown in below example:
describe("long asynchronous specs", function() {
var originalTimeout;
beforeEach(function() {
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 60000; // 60 second (use value as per need)
});
it("takes a long time", function(done) {
setTimeout(function() {
done();
}, 9000);
});
afterEach(function() {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
});
});
It still depend on the network as well how fast your promise returns the value. I would suggest you to change the value for jasmine.DEFAULT_TIMEOUT_INTERVAL only in test where you are working with actual dependecies.
I hope this will help.
Related
I've been reading some articles, and posts here on Stack Overflow about when I should mock a function and when I shouldn't, but I have a case where I'm not sure about what to do.
I have a UserService class which uses dependency injection concept to receive dependencies through its constructor.
class UserService {
constructor(userRepository) {
this.userRepository = userRepository;
}
async getUserByEmail(userEmail) {
// would perform some validations to check if the value is an e-mail
const user = await this.userRepository.findByEmail(email);
return user;
}
async createUser(userData) {
const isEmailInUse = await this.getUserByEmail(userData.email);
if(isEmailInUse) {
return "error";
}
const user = await this.userRepository.create(userData);
return user;
}
}
I want to test if the createUser method works properly, and for my tests, I created a fake userRepository which is basically a object with mocked methods that I will use while instantiating UserService Class
const UserService = require('./UserService.js');
describe("User Service tests", () => {
let userService;
let userRepository;
beforeEach(() => {
userRepository = {
findOne: jest.fn(),
create: jest.fn(),
}
userService = new UserService(userRepository);
});
afterEach(() => {
resetAllMocks();
});
describe("createUser", () => {
it("should be able to create a new user", async () => {
const newUserData = { name: 'User', email: 'user#test.com.br' }
const user = { id: 1, name: 'User', email: 'user#test.com.br' }
userRepository.create.mockResolvedValue(user);
const result = await userService.createUser();
expect(result).toStrictEqual(user);
})
})
})
Note that in the createUser method, there is a call to the getUserByEmail method which is also a method of UserService class, and that is where I got confused.
Should I mock the getUserByEmail method even it is a method of the class I'm testing? If it is not the correct approach, what should I do?
You should almost always prefer not to mock parts of the thing you're supposed to be testing, in this case UserService. To illustrate why, consider these two tests:
Provides a test double implementation for findByEmail on the repo object:
it("throws an error if the user already exists", async () => {
const email = "foo#bar.baz";
const user = { email, name: "Foo Barrington" };
const service = new UserService({
findByEmail: (_email) => Promise.resolve(_email === email ? user : null),
});
await expect(service.createUser(user)).rejects.toThrow("User already exists");
});
Stubs out the service's own getUserByEmail method:
it("throws an error if the user already exists", async () => {
const email = "foo#bar.baz";
const user = { email, name: "Foo Barrington" };
const service = new UserService({});
service.getUserByEmail = (_email) => Promise.resolve(_email === email ? user : null);
await expect(service.createUser(user)).rejects.toThrow("User already exists");
});
For your current implementation, both pass just fine. But let's think about how things might change.
Imagine we need to enrich the user model getUserByEmail provides at some point:
async getUserByEmail(userEmail) {
const user = await this.userRepository.findByEmail(userEmail);
user.moreStuff = await.this.userRepository.getSomething(user.id);
return user;
}
Obviously we don't need this extra data just to know whether or not the user exists, so we factor out the basic user object retrieval:
async getUserByEmail(userEmail) {
const user = await this._getUser(userEmail);
user.moreStuff = await.this.userRepository.getSomething(user.id);
return user;
}
async createUser(userData) {
if (await this._getUser(userData.email)) {
throw new Error("User already exists");
}
return this.userRepository.create(userData);
}
async _getUser(userEmail) {
return this.userRepository.findByEmail(userEmail);
}
If we were using test 1, we wouldn't have to change it at all - we're still consuming findByEmail on the repo, the fact that the internal implementation has changed is opaque to our test. But with test 2, that's now failing even though the code still does the same things. This is a false negative; the functionality works but the test fails.
In fact you could have applied that refactor, extracting _getUser, prior to a new feature making the need so clear; the fact that createUser uses getUserByEmail directly reflects accidental duplication of this.userRepository.findByEmail(email) - they have different reasons to change.
Or imagine we make some change that breaks getUserByEmail. Let's simulate a problem with the enrichment, for example:
async getUserByEmail(userEmail) {
const user = await this.userRepository.findByEmail(userEmail);
throw new Error("lol whoops!");
return user;
}
If we're using test 1, our test for createUser fails too, but that's the correct outcome! The implementation is broken, a user cannot be created. With test 2 we have a false positive; the test passes but the functionality doesn't work.
In this case, you could say that it's better to see that only getUserByEmail is failing because that's where the problem is, but I'd contend that would be pretty confusing when you looked at the code: "createUser calls that method too, but the tests say it's fine...".
You shouldn't mock any of these functions since its creating users and reading data from the database. If you mock them then what's the point of the test. In other words, you wouldn't know if your app is working correctly with the database or not. Anyway, I would mock functions such as the functions that send emails and so on. Don't mock the functions that are the heart of the application. You should have a database for testing and another one for production.
I have a vue.js app that uses firebase authentication. When unit-testing my Login.vue component, I mock firebase auth by mocking the config file I use to initialize firebase (named firebase.config). I should note that firebase.auth() is being exported from my config file as auth, hence the use of fb.auth and not fb.auth() in my code.
One of my tests checks that if the user is authenticated but not emailVerified, an email dialog is displayed. The dialog is not part of the test, but whether the dialogEmail data property is being set to true is what the test is asserting.
As part of the sign-in process, I set my firebase authentication state persistence to 'session'. Though this combination is not an issue for my app, it's giving me an issue in my test.
From Login.vue (abbreviated component code):
<template>
<v-btn #click="signInUser(email, password)">
Sign in
<v/btn>
</template>
<script>
import * as fb from '#/firebase.config';
export default {
data () {
return {
email: '',
password: '',
};
},
methods: {
async signInUser (email, password) {
try {
await fb.auth.setPersistence('session');
const { user } = await fb.auth.signInWithEmailAndPassword(email, password);
if (user.emailVerified) {
this.$router.push('/home');
} else {
this.dialogEmail = true;
}
} catch (err) {
// ...error handling
}
},
},
};
</script>
From login.spec.js:
import Vuetify from 'vuetify';
import { mount, createLocalVue } from '#vue/test-utils';
import Login from '#/views/Login';
jest.mock('#/firebase.config', () => ({
auth: {
setPersistence: () => Promise.resolve(),
signInWithEmailAndPassword: () => Promise.resolve({ user: { emailVerified: false } }),
},
}));
const localVue = createLocalVue();
const $router = { push: jest.fn() };
describe('login.vue', () => {
let vuetify;
beforeEach(() => {
vuetify = new Vuetify();
});
const mountFunction = () => mount(Login, {
localVue,
vuetify,
mocks: { $router },
});
it('shows dialog if user email not verified on sign in', async () => {
expect.assertions(1);
const wrapper = mountFunction();
const signInButton = wrapper.find('[data-test="signIn"]');
await wrapper.setData({ email: 'foo#bar.org', password: 'FooBarBaz' });
await signInButton.trigger('click');
expect(wrapper.vm.dialogEmail).toBe(true);
});
});
My app works fine. My test does not. It fails with an output of:
expect(received).toBe(expected) // Object.is equality
Expected: true
Received: false
However, my test WILL pass on one of two conditions:
I remove either of my await code snippets in my signInUser() method. This results in only one call to my mock function, which somehow helps.
I add a short wait to my test just before my assertion, like this (abbreviated test code):
await signInButton.trigger('click');
await new Promise(r => setTimeout(r, 0)); // my added code
expect(wrapper.vm.dialogEmail).toBe(true);
I'm elated that option #2 works, so that's what I'm doing. The question I have, however, is why does this extra timeout work, and why is it required? I'd like to understand what's happening, so I'm asking about it here.
In sign in user function, you are waiting for 2 promises to be resolved:
await fb.auth.setPersistence('session');
const { user } = await fb.auth.signInWithEmailAndPassword(email, password);
Inside test you added await signInButton.trigger('click'); but this will just trigger the function call, but not waiting for until its complete.
So you must add mock promise resolve inside test after you called the signin function.
setTimeout with 0 will add the promise at the end of the tasks queue so the test will be delayed there until the end of the function.
I'm trying to test an API with jest. Originally all my tests wherein one file and all tests where passing. I wanted to separate my tests into different files. To do this I'm trying to use a global setup file with beforeEach and afterEach. The problem is that sometimes I run the tests and they pass and other times they fail (different test pass and different fail each time).
package.json
{
"jest": {
"verbose": true,
"bail": false,
"preset": "#shelf/jest-mongodb",
"setupFilesAfterEnv": [
"<rootDir>/jest.setup.js"
],
"globals": {
"authHeaders": {},
"authId": null
}
},
}
jest.setup.js
const app = require("./index")
const supertest = require("supertest")
const request = supertest(app)
const db = require("./src/db")
const jwtDecode = require("jwt-decode")
const getAuthToken = require("./src/utils/getAuthToken")
let token
let decodedJwt
const getAuthHeaders = token => ({
Authorization: `Bearer ${token}`
})
beforeEach(async done => {
//setup globals available in all tests
token = await getAuthToken()
token = JSON.parse(token)["access_token"]
decodedJwt = jwtDecode(token)
authId = decodedJwt.sub
authHeaders = getAuthHeaders(token)
// Ensure that we have a users collection with our test user
// who can then call the api
await request
.post("/v1/users")
.set(authHeaders)
.send({ email: "test#example.com" })
.expect(200)
done()
})
afterEach(async done => {
// Delete the users collection
await db.instance.dropUsers()
await db.instance.dropAgencies()
done()
})
users.test.js
const app = require("../../../../index")
const supertest = require("supertest")
const request = supertest(app)
const faker = require("faker")
describe("/v1/users/:id", () => {
test("should return an error when the users do not have a mutual agency", async done => {
if (!authHeaders || !authId) {
expect(true).toBe(false)
}
let testUserId = null
let email = faker.internet.email()
await request
.post("/v1/users")
.set(authHeaders)
.send({ email })
.expect(200)
.then(({ body }) => (testUserId = body._id))
await request
.get("/v1/users/" + testUserId)
.set(authHeaders)
.expect(200)
.then(({ body }) => {
expect(body).toHaveProperty("error")
expect(body.error).toHaveProperty("message")
})
done()
})
})
I can run the above once and it will pass and run again moments later and it will fail.
Edit:
After further investigation it seems that the issue is something to do with the afterEach function not dropping the Users collection. When I console log the response of the request made in the beforeEach in the jest.setup.js file I'm getting an error response saying that the user for the given email already exists.
The issue was caused by multiple tests running at the same time, adding the --runInBand flag (which forces test to be run serially) fixes the issue.
The error in my thinking was I assumed it was safe to add a user record to the database beforeEach and delete it afterEach without considering that if two tests run at the same time one would always fail as a user would already exist in the database.
In light of this I think it would be an idea to refactor my setup file so that my test user is only created once for all tests but I need to consider the implications of doing this.
EDIT:
I have found that I can run test successfully without the runInBand flag by switching out my atlas cloud database in favour of mongodb-memory-server during tests.
I have tried almost any example provided in docs but I can't run it.
I want to make a request to a specific url with axios (or fetch method) every 60 seconds and process the data in the background. In other words I want something common like:
this.getPageInterval = setInterval(() => {
const json = await (await fetch(this.fetchURL)).json();
...// etc
}, 60000)
can happen when the app is in background.
my console.log says 'task registered' but it feels like this block of code never triggers(global scope):
BackgroundFetch.setMinimumIntervalAsync(5);
const taskName = 'test-background-fetch';
TaskManager.defineTask(taskName, async () => {
console.log('background fetch running');
try {
const receivedNewData = await (await fetch(this.fetchUri)).json();
console.log('receivedNewData', receivedNewData)
return receivedNewData ? BackgroundFetch.Result.NewData : BackgroundFetch.Result.NoData;
let isRegistered = await TaskManager.isTaskRegisteredAsync(taskName);
console.log("isRegistered: ", isRegistered);
} catch (error) {
return BackgroundFetch.Result.Failed;
}
console.log("BackgroundFetch.Result.NewData", BackgroundFetch.Result.NewData);
return BackgroundFetch.Result.NewData;
});
and in my class component:
await BackgroundFetch.registerTaskAsync(taskName, {
setMinimumIntervalAsync: 5,
stopOnTerminate: false
});
await BackgroundFetch.setMinimumIntervalAsync(5);
alert('task registered');
console.log('task registered');
It seems you're not registering your task in your class component. Is taskname defined as 'test-background-fetch' in your component? You need to call exactly the same task name.
Also some phones are somewhat problematic with background tasks. I never tried it on expo, but on React Native some brands had to go through some extra setup.
Also note that in the documentation it says it only works when backgrounded. Terminated apps wont run the background function.
I'm using NestJS CQRS recipe in order to manage interactions between two entities: User and UserProfile. The architecture is an API Gateway NestJS server + a NestJS server for each microservice (User, UserProfile, etc.).
I have already set up basic interactions through User and UserProfile modules on API Gateway with their own sagas/events/commands:
When a user is created, a user profile is created
When the user profile creation fails, the previously created user is deleted
In details:
In User module, CreateUser command raises a UserCreated event that is intercepted by User saga, which will trigger CreateUserProfile command (from UserProfile module).
If the latter fails, a UserProfileFailedToCreate event is raised and intercepted by UserProfile saga, which will trigger DeleteUser command (from User module).
Everything works fine.
If the CreateUser command fails, I resolve(Promise.reject(new HttpException(error, error.status)) which indicates to the end user that something went wrong during the user creation.
My problem is that I cannot replicate the same behavior for the CreateUserProfile command since the HTTP request promise has already been resolved from the first command, obviously.
So my question is: is there any way to make a command fail if a subsequent command fails in the saga? I understand that the HTTP request is totally disconnected from any subsequent commands triggered by a saga, but I want to know if anybody has already played with events or something else here to replicate this data flow?
One of the reasons I'm using CQRS, besides having a much cleaner code for data interactions among microservices, is to be able to rollback repositories actions in case any of the chained commands fails, which works fine. But I need a way to indicate to the end user that the chain went through an issue and was rollbacked.
UserController.ts
#Post('createUser')
async createUser(#Body() createUserDto: CreateUserDto): Promise<{user: IAuthUser, token: string}> {
const { authUser } = await this.authService.createAuthUser(createUserDto);
// this is executed after resolve() in CreateUserCommand
return {user: authUser, token: this.authService.createAccessTokenFromUser(authUser)};
}
UserService.ts
async createAuthUser(createUserDto: CreateUserDto): Promise<{authUser: IAuthUser}> {
return await this.commandBus
.execute(new CreateAuthUserCommand(createUserDto))
.catch(error => { throw new HttpException(error, error.status); });
}
CreateUserCommand.ts
async execute(command: CreateAuthUserCommand, resolve: (value?) => void) {
const { createUserDto } = command;
const createAuthUserDto: CreateAuthUserDto = {
email: createUserDto.email,
password: createUserDto.password,
phoneNumber: createUserDto.phoneNumber,
};
try {
const user = this.publisher.mergeObjectContext(
await this.client
.send<IAuthUser>({ cmd: 'createAuthUser' }, createAuthUserDto)
.toPromise()
.then((dbUser: IAuthUser) => {
const {password, passwordConfirm, ...publicUser} = Object.assign(dbUser, createUserDto);
return new AuthUser(publicUser);
}),
);
user.notifyCreated();
user.commit();
resolve(user); // <== This makes the HTTP request return its reponse
} catch (error) {
resolve(Promise.reject(error));
}
}
UserSagas.ts
authUserCreated = (event$: EventObservable<any>): Observable<ICommand> => {
return event$
.ofType(AuthUserCreatedEvent)
.pipe(
map(event => {
const createUserProfileDto: CreateUserProfileDto = {
avatarUrl: '',
firstName: event.authUser.firstName,
lastName: event.authUser.lastName,
nationality: '',
userId: event.authUser.id,
username: event.authUser.username,
};
return new CreateUserProfileCommand(createUserProfileDto);
}),
);
}
CreateUserProfileCommand.ts
async execute(command: CreateUserProfileCommand, resolve: (value?) => void) {
const { createUserProfileDto } = command;
try {
const userProfile = this.publisher.mergeObjectContext(
await this.client
.send<IUserProfile>({ cmd: 'createUserProfile' }, createUserProfileDto)
.toPromise()
.then((dbUserProfile: IUserProfile) => new UserProfile(dbUserProfile)),
);
userProfile.notifyCreated();
userProfile.commit();
resolve(userProfile);
} catch (error) {
const userProfile = this.publisher.mergeObjectContext(new UserProfile({id: createUserProfileDto.userId} as IUserProfile));
userProfile.notifyFailedToCreate();
userProfile.commit();
resolve(Promise.reject(new HttpException(error, 500)).catch(() => {}));
}
}
UserProfileSagas.ts
userProfileFailedToCreate = (event$: EventObservable<any>): Observable<ICommand> => {
return event$
.ofType(UserProfileFailedToCreateEvent)
.pipe(
map(event => {
return new DeleteAuthUserCommand(event.userProfile);
}),
);
}
DeleteUserCommand.ts
async execute(command: DeleteAuthUserCommand, resolve: (value?) => void) {
const { deleteAuthUserDto } = command;
try {
const user = this.publisher.mergeObjectContext(
await this.client
.send<IAuthUser>({ cmd: 'deleteAuthUser' }, deleteAuthUserDto)
.toPromise()
.then(() => new AuthUser({} as IAuthUser)),
);
user.notifyDeleted();
user.commit();
resolve(user);
} catch (error) {
resolve(Promise.reject(new HttpException(error, error.status)).catch(() => {}));
}
}
In DDD terms your creation of User and UserProfile constitutes a business transaction - a group of business operations/rules that must be consistent - that spans multiple microservices.
In that case returning the database User before the UserProfile has been created means you return data in an inconsistent state. This is not necessarily wrong, but you should handle this appropriately in the client if you do it like that.
I see three possible ways to deal with this scenario:
You let the Sagas run until they have executed commands that indicate the business transaction has ended, only then resolve an outcome for the client indicating either success or failure (e.g. in error details you can report which steps succeeded and which did not). So you do not yet resolve in CreateAuthUserCommand.
If it can take a long time for the UserProfile to be created (it could even have to be manually validated by a Moderator) then you might want to resolve the User in CreateAuthUserCommand and then subsequently have the client subscribe to UserProfile-related events. You need a mechanism for that, but it decouples the client from the running transaction, and it can do other stuff.
Alternatively you could break up the business transaction into two parts for which the client sends separate requests: one creates/returns the authenticated User, and the other returns the created UserProfile. Though it seems that User + UserProfile belong to the same bounded context, the fact that they reside in two different microservices may indicate they are not (in this case I think the first microservice really is for Authentication and the other for UserProfiles which indicate different bounded contexts to me). Best-practice is to have a microservice implement their own encapsulated bounded context.
(Note: Answered an old question in hopes it is informative to others)