mirror of
https://github.com/logto-io/logto.git
synced 2025-01-27 21:39:16 -05:00
test(test): add passwordless integration test (#1697)
* test(test): add paswordless integration test add passwordless integration test * fix(test): add clean up add clean up
This commit is contained in:
parent
c7e311c344
commit
a2e4729aaa
7 changed files with 326 additions and 12 deletions
|
@ -1,3 +1,6 @@
|
|||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
import {
|
||||
ConnectorError,
|
||||
ConnectorErrorCodes,
|
||||
|
@ -71,6 +74,11 @@ export default class MockMailConnector implements EmailConnectorInstance<MockMai
|
|||
)
|
||||
);
|
||||
|
||||
await fs.writeFile(
|
||||
path.join('/tmp', 'logto_mock_passcode_record.txt'),
|
||||
JSON.stringify({ address, code: data.code, type }) + '\n'
|
||||
);
|
||||
|
||||
return { address, data };
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
import {
|
||||
ConnectorError,
|
||||
ConnectorErrorCodes,
|
||||
|
@ -71,6 +74,11 @@ export default class MockSmsConnector implements SmsConnectorInstance<MockSmsCon
|
|||
)
|
||||
);
|
||||
|
||||
await fs.writeFile(
|
||||
path.join('/tmp', 'logto_mock_passcode_record.txt'),
|
||||
JSON.stringify({ phone, code: data.code, type }) + '\n'
|
||||
);
|
||||
|
||||
return { phone, data };
|
||||
};
|
||||
}
|
||||
|
|
|
@ -49,3 +49,111 @@ export const consent = async (interactionCookie: string) =>
|
|||
followRedirect: false,
|
||||
})
|
||||
.json<RedirectResponse>();
|
||||
|
||||
export const sendRegisterUserWithEmailPasscode = (email: string, interactionCookie: string) =>
|
||||
api.post('session/register/passwordless/email/send-passcode', {
|
||||
headers: {
|
||||
cookie: interactionCookie,
|
||||
},
|
||||
json: {
|
||||
email,
|
||||
},
|
||||
});
|
||||
|
||||
export const verifyRegisterUserWithEmailPasscode = (
|
||||
email: string,
|
||||
code: string,
|
||||
interactionCookie: string
|
||||
) =>
|
||||
api
|
||||
.post('session/register/passwordless/email/verify-passcode', {
|
||||
headers: {
|
||||
cookie: interactionCookie,
|
||||
},
|
||||
json: {
|
||||
email,
|
||||
code,
|
||||
},
|
||||
})
|
||||
.json<RedirectResponse>();
|
||||
|
||||
export const sendSignInUserWithEmailPasscode = (email: string, interactionCookie: string) =>
|
||||
api.post('session/sign-in/passwordless/email/send-passcode', {
|
||||
headers: {
|
||||
cookie: interactionCookie,
|
||||
},
|
||||
json: {
|
||||
email,
|
||||
},
|
||||
});
|
||||
|
||||
export const verifySignInUserWithEmailPasscode = (
|
||||
email: string,
|
||||
code: string,
|
||||
interactionCookie: string
|
||||
) =>
|
||||
api
|
||||
.post('session/sign-in/passwordless/email/verify-passcode', {
|
||||
headers: {
|
||||
cookie: interactionCookie,
|
||||
},
|
||||
json: {
|
||||
email,
|
||||
code,
|
||||
},
|
||||
})
|
||||
.json<RedirectResponse>();
|
||||
|
||||
export const sendRegisterUserWithSmsPasscode = (phone: string, interactionCookie: string) =>
|
||||
api.post('session/register/passwordless/sms/send-passcode', {
|
||||
headers: {
|
||||
cookie: interactionCookie,
|
||||
},
|
||||
json: {
|
||||
phone,
|
||||
},
|
||||
});
|
||||
|
||||
export const verifyRegisterUserWithSmsPasscode = (
|
||||
phone: string,
|
||||
code: string,
|
||||
interactionCookie: string
|
||||
) =>
|
||||
api
|
||||
.post('session/register/passwordless/sms/verify-passcode', {
|
||||
headers: {
|
||||
cookie: interactionCookie,
|
||||
},
|
||||
json: {
|
||||
phone,
|
||||
code,
|
||||
},
|
||||
})
|
||||
.json<RedirectResponse>();
|
||||
|
||||
export const sendSignInUserWithSmsPasscode = (phone: string, interactionCookie: string) =>
|
||||
api.post('session/sign-in/passwordless/sms/send-passcode', {
|
||||
headers: {
|
||||
cookie: interactionCookie,
|
||||
},
|
||||
json: {
|
||||
phone,
|
||||
},
|
||||
});
|
||||
|
||||
export const verifySignInUserWithSmsPasscode = (
|
||||
phone: string,
|
||||
code: string,
|
||||
interactionCookie: string
|
||||
) =>
|
||||
api
|
||||
.post('session/sign-in/passwordless/sms/verify-passcode', {
|
||||
headers: {
|
||||
cookie: interactionCookie,
|
||||
},
|
||||
json: {
|
||||
phone,
|
||||
code,
|
||||
},
|
||||
})
|
||||
.json<RedirectResponse>();
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
|
||||
import { User } from '@logto/schemas';
|
||||
import { assert } from '@silverhand/essentials';
|
||||
|
||||
|
@ -5,6 +8,8 @@ import {
|
|||
createUser,
|
||||
registerUserWithUsernameAndPassword,
|
||||
signInWithUsernameAndPassword,
|
||||
updateConnectorConfig,
|
||||
enableConnector,
|
||||
} from '@/api';
|
||||
import MockClient from '@/client';
|
||||
import { generateUsername, generatePassword } from '@/utils';
|
||||
|
@ -61,3 +66,25 @@ export const signIn = async (username: string, password: string) => {
|
|||
|
||||
assert(client.isAuthenticated, new Error('Sign in failed'));
|
||||
};
|
||||
|
||||
export const setUpConnector = async (connectorId: string, config: Record<string, unknown>) => {
|
||||
await updateConnectorConfig(connectorId, config);
|
||||
const connector = await enableConnector(connectorId);
|
||||
assert(connector.enabled, new Error('Connector Setup Failed'));
|
||||
};
|
||||
|
||||
type PasscodeRecord = {
|
||||
phone?: string;
|
||||
address?: string;
|
||||
code: string;
|
||||
type: string;
|
||||
};
|
||||
|
||||
export const readPasscode = async (): Promise<PasscodeRecord> => {
|
||||
const buffer = await fs.readFile(path.join('/tmp', 'logto_mock_passcode_record.txt'));
|
||||
const content = buffer.toString();
|
||||
|
||||
// For test use only
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
return JSON.parse(content) as PasscodeRecord;
|
||||
};
|
||||
|
|
|
@ -11,3 +11,10 @@ export const generatePassword = () => `pwd_${crypto.randomUUID()}`;
|
|||
|
||||
export const generateResourceName = () => `res_${crypto.randomUUID()}`;
|
||||
export const generateResourceIndicator = () => `https://${crypto.randomUUID()}.logto.io`;
|
||||
export const generateEmail = () => `${crypto.randomUUID()}@logto.io`;
|
||||
|
||||
export const generatePhone = () => {
|
||||
const array = new Uint32Array(1);
|
||||
|
||||
return crypto.getRandomValues(array).join('');
|
||||
};
|
||||
|
|
|
@ -29,16 +29,6 @@ import {
|
|||
* for testing updating configs and enabling/disabling for now.
|
||||
*/
|
||||
test('connector set-up flow', async () => {
|
||||
/*
|
||||
* List connectors after initializing a new Logto instance
|
||||
*/
|
||||
const allConnectors = await listConnectors();
|
||||
|
||||
// There should be no connectors, or all connectors should be disabled.
|
||||
for (const connectorDto of allConnectors) {
|
||||
expect(connectorDto.enabled).toBeFalsy();
|
||||
}
|
||||
|
||||
/*
|
||||
* Set up social/SMS/email connectors
|
||||
*/
|
||||
|
|
|
@ -1,5 +1,25 @@
|
|||
import { registerNewUser, signIn } from '@/helpers';
|
||||
import { generateUsername, generatePassword } from '@/utils';
|
||||
import { assert } from '@silverhand/essentials';
|
||||
|
||||
import {
|
||||
mockEmailConnectorId,
|
||||
mockEmailConnectorConfig,
|
||||
mockSmsConnectorId,
|
||||
mockSmsConnectorConfig,
|
||||
} from '@/__mocks__/connectors-mock';
|
||||
import {
|
||||
sendRegisterUserWithEmailPasscode,
|
||||
verifyRegisterUserWithEmailPasscode,
|
||||
sendSignInUserWithEmailPasscode,
|
||||
verifySignInUserWithEmailPasscode,
|
||||
sendRegisterUserWithSmsPasscode,
|
||||
verifyRegisterUserWithSmsPasscode,
|
||||
sendSignInUserWithSmsPasscode,
|
||||
verifySignInUserWithSmsPasscode,
|
||||
disableConnector,
|
||||
} from '@/api';
|
||||
import MockClient from '@/client';
|
||||
import { registerNewUser, signIn, setUpConnector, readPasscode } from '@/helpers';
|
||||
import { generateUsername, generatePassword, generateEmail, generatePhone } from '@/utils';
|
||||
|
||||
describe('username and password flow', () => {
|
||||
const username = generateUsername();
|
||||
|
@ -13,3 +33,149 @@ describe('username and password flow', () => {
|
|||
await expect(signIn(username, password)).resolves.not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('email passwordless flow', () => {
|
||||
beforeAll(async () => {
|
||||
await setUpConnector(mockEmailConnectorId, mockEmailConnectorConfig);
|
||||
});
|
||||
|
||||
// Since we can not create a email register user throw admin. Have to run the register then sign-in concurrently.
|
||||
const email = generateEmail();
|
||||
|
||||
it('register with email', async () => {
|
||||
const client = new MockClient();
|
||||
|
||||
await client.initSession();
|
||||
assert(client.interactionCookie, new Error('Session not found'));
|
||||
|
||||
await expect(
|
||||
sendRegisterUserWithEmailPasscode(email, client.interactionCookie)
|
||||
).resolves.not.toThrow();
|
||||
|
||||
const passcodeRecord = await readPasscode();
|
||||
|
||||
expect(passcodeRecord).toMatchObject({
|
||||
address: email,
|
||||
type: 'Register',
|
||||
});
|
||||
|
||||
const { code } = passcodeRecord;
|
||||
|
||||
const { redirectTo } = await verifyRegisterUserWithEmailPasscode(
|
||||
email,
|
||||
code,
|
||||
client.interactionCookie
|
||||
);
|
||||
|
||||
await client.processSession(redirectTo);
|
||||
|
||||
expect(client.isAuthenticated).toBeTruthy();
|
||||
});
|
||||
|
||||
it('sign-in with email', async () => {
|
||||
const client = new MockClient();
|
||||
|
||||
await client.initSession();
|
||||
assert(client.interactionCookie, new Error('Session not found'));
|
||||
|
||||
await expect(
|
||||
sendSignInUserWithEmailPasscode(email, client.interactionCookie)
|
||||
).resolves.not.toThrow();
|
||||
|
||||
const passcodeRecord = await readPasscode();
|
||||
|
||||
expect(passcodeRecord).toMatchObject({
|
||||
address: email,
|
||||
type: 'SignIn',
|
||||
});
|
||||
|
||||
const { code } = passcodeRecord;
|
||||
|
||||
const { redirectTo } = await verifySignInUserWithEmailPasscode(
|
||||
email,
|
||||
code,
|
||||
client.interactionCookie
|
||||
);
|
||||
|
||||
await client.processSession(redirectTo);
|
||||
|
||||
expect(client.isAuthenticated).toBeTruthy();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
void disableConnector(mockEmailConnectorId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sms passwordless flow', () => {
|
||||
beforeAll(async () => {
|
||||
await setUpConnector(mockSmsConnectorId, mockSmsConnectorConfig);
|
||||
});
|
||||
|
||||
// Since we can not create a sms register user throw admin. Have to run the register then sign-in concurrently.
|
||||
const phone = generatePhone();
|
||||
|
||||
it('register with sms', async () => {
|
||||
const client = new MockClient();
|
||||
|
||||
await client.initSession();
|
||||
assert(client.interactionCookie, new Error('Session not found'));
|
||||
|
||||
await expect(
|
||||
sendRegisterUserWithSmsPasscode(phone, client.interactionCookie)
|
||||
).resolves.not.toThrow();
|
||||
|
||||
const passcodeRecord = await readPasscode();
|
||||
|
||||
expect(passcodeRecord).toMatchObject({
|
||||
phone,
|
||||
type: 'Register',
|
||||
});
|
||||
|
||||
const { code } = passcodeRecord;
|
||||
|
||||
const { redirectTo } = await verifyRegisterUserWithSmsPasscode(
|
||||
phone,
|
||||
code,
|
||||
client.interactionCookie
|
||||
);
|
||||
|
||||
await client.processSession(redirectTo);
|
||||
|
||||
expect(client.isAuthenticated).toBeTruthy();
|
||||
});
|
||||
|
||||
it('sign-in with email', async () => {
|
||||
const client = new MockClient();
|
||||
|
||||
await client.initSession();
|
||||
assert(client.interactionCookie, new Error('Session not found'));
|
||||
|
||||
await expect(
|
||||
sendSignInUserWithSmsPasscode(phone, client.interactionCookie)
|
||||
).resolves.not.toThrow();
|
||||
|
||||
const passcodeRecord = await readPasscode();
|
||||
|
||||
expect(passcodeRecord).toMatchObject({
|
||||
phone,
|
||||
type: 'SignIn',
|
||||
});
|
||||
|
||||
const { code } = passcodeRecord;
|
||||
|
||||
const { redirectTo } = await verifySignInUserWithSmsPasscode(
|
||||
phone,
|
||||
code,
|
||||
client.interactionCookie
|
||||
);
|
||||
|
||||
await client.processSession(redirectTo);
|
||||
|
||||
expect(client.isAuthenticated).toBeTruthy();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
void disableConnector(mockSmsConnectorId);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Reference in a new issue