0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-30 20:33:54 -05:00

refactor!: update mock connector file paths

This commit is contained in:
Gao Sun 2024-01-29 21:33:12 +08:00
parent 6d0f95739c
commit 6befe60149
No known key found for this signature in database
GPG key ID: 13EBE123E4773688
23 changed files with 122 additions and 91 deletions

View file

@ -0,0 +1,10 @@
---
"@logto/connector-mock-standard-email": major
"@logto/connector-mock-email": major
"@logto/connector-mock-sms": major
---
update `writeFile` path according to the connector type
- SMS connector: `/tmp/logto_mock_sms_record.txt`
- Email connector: `/tmp/logto_mock_email_record.txt`

View file

@ -0,0 +1,5 @@
---
"@logto/connector-kit": minor
---
add `mockConnectorFilePaths` and deprecate `mockSmsVerificationCodeFileName`

View file

@ -1,6 +1,5 @@
import { assert } from '@silverhand/essentials'; import { assert } from '@silverhand/essentials';
import fs from 'node:fs/promises'; import fs from 'node:fs/promises';
import path from 'node:path';
import type { import type {
GetConnectorConfig, GetConnectorConfig,
@ -13,6 +12,7 @@ import {
ConnectorErrorCodes, ConnectorErrorCodes,
validateConfig, validateConfig,
ConnectorType, ConnectorType,
mockConnectorFilePaths,
} from '@logto/connector-kit'; } from '@logto/connector-kit';
import { defaultMetadata } from './constant.js'; import { defaultMetadata } from './constant.js';
@ -36,8 +36,8 @@ const sendMessage =
); );
await fs.writeFile( await fs.writeFile(
path.join('/tmp', 'logto_mock_verification_code_record.txt'), mockConnectorFilePaths.Email,
JSON.stringify({ address: to, code: payload.code, type }) + '\n' JSON.stringify({ address: to, code: payload.code, type, payload }) + '\n'
); );
return { address: to, data: payload }; return { address: to, data: payload };

View file

@ -1,6 +1,5 @@
import { assert } from '@silverhand/essentials'; import { assert } from '@silverhand/essentials';
import fs from 'node:fs/promises'; import fs from 'node:fs/promises';
import path from 'node:path';
import type { import type {
GetConnectorConfig, GetConnectorConfig,
@ -13,6 +12,7 @@ import {
ConnectorErrorCodes, ConnectorErrorCodes,
validateConfig, validateConfig,
ConnectorType, ConnectorType,
mockConnectorFilePaths,
} from '@logto/connector-kit'; } from '@logto/connector-kit';
import { defaultMetadata } from './constant.js'; import { defaultMetadata } from './constant.js';
@ -36,8 +36,8 @@ const sendMessage =
); );
await fs.writeFile( await fs.writeFile(
path.join('/tmp', 'logto_mock_verification_code_record.txt'), mockConnectorFilePaths.Email,
JSON.stringify({ address: to, code: payload.code, type }) + '\n' JSON.stringify({ address: to, code: payload.code, type, payload }) + '\n'
); );
return { address: to, data: payload }; return { address: to, data: payload };

View file

@ -1,6 +1,5 @@
import { assert } from '@silverhand/essentials'; import { assert } from '@silverhand/essentials';
import fs from 'node:fs/promises'; import fs from 'node:fs/promises';
import path from 'node:path';
import type { import type {
GetConnectorConfig, GetConnectorConfig,
@ -13,6 +12,7 @@ import {
ConnectorErrorCodes, ConnectorErrorCodes,
validateConfig, validateConfig,
ConnectorType, ConnectorType,
mockConnectorFilePaths,
} from '@logto/connector-kit'; } from '@logto/connector-kit';
import { defaultMetadata } from './constant.js'; import { defaultMetadata } from './constant.js';
@ -36,8 +36,8 @@ const sendMessage =
); );
await fs.writeFile( await fs.writeFile(
path.join('/tmp', 'logto_mock_verification_code_record.txt'), mockConnectorFilePaths.Sms,
JSON.stringify({ phone: to, code: payload.code, type }) + '\n' JSON.stringify({ phone: to, code: payload.code, type, payload }) + '\n'
); );
return { phone: to, data: payload }; return { phone: to, data: payload };

View file

@ -14,8 +14,6 @@ import type Queries from '#src/tenants/Queries.js';
import { type ConnectorLibrary } from './connector.js'; import { type ConnectorLibrary } from './connector.js';
const invitationLinkPath = '/invitation';
/** /**
* The ending statuses of an organization invitation per RFC 0003. It means that the invitation * The ending statuses of an organization invitation per RFC 0003. It means that the invitation
* status cannot be changed anymore. * status cannot be changed anymore.

View file

@ -1,15 +1,12 @@
import fs from 'node:fs/promises'; import fs from 'node:fs/promises';
import { createServer, type RequestListener } from 'node:http'; import { createServer, type RequestListener } from 'node:http';
import path from 'node:path';
import { mockSmsVerificationCodeFileName } from '@logto/connector-kit'; import { mockConnectorFilePaths } from '@logto/connector-kit';
import { RequestError } from 'got'; import { RequestError } from 'got';
import { createUser } from '#src/api/index.js'; import { createUser } from '#src/api/index.js';
import { generateUsername } from '#src/utils.js'; import { generateUsername } from '#src/utils.js';
const temporaryVerificationCodeFilePath = path.join('/tmp', mockSmsVerificationCodeFileName);
export const createUserByAdmin = async ( export const createUserByAdmin = async (
username?: string, username?: string,
password?: string, password?: string,
@ -33,8 +30,10 @@ type VerificationCodeRecord = {
type: string; type: string;
}; };
export const readVerificationCode = async (): Promise<VerificationCodeRecord> => { export const readVerificationCode = async (
const buffer = await fs.readFile(temporaryVerificationCodeFilePath); forType: keyof typeof mockConnectorFilePaths
): Promise<VerificationCodeRecord> => {
const buffer = await fs.readFile(mockConnectorFilePaths[forType]);
const content = buffer.toString(); const content = buffer.toString();
// For test use only // For test use only
@ -42,9 +41,11 @@ export const readVerificationCode = async (): Promise<VerificationCodeRecord> =>
return JSON.parse(content) as VerificationCodeRecord; return JSON.parse(content) as VerificationCodeRecord;
}; };
export const removeVerificationCode = async (): Promise<void> => { export const removeVerificationCode = async (
forType: keyof typeof mockConnectorFilePaths
): Promise<void> => {
try { try {
await fs.unlink(temporaryVerificationCodeFilePath); await fs.unlink(mockConnectorFilePaths[forType]);
} catch { } catch {
// Do nothing // Do nothing
} }

View file

@ -101,7 +101,9 @@ export const resetPassword = async (
...profile, ...profile,
}); });
const { code: verificationCode } = await readVerificationCode(); const { code: verificationCode } = await readVerificationCode(
'email' in profile ? 'Email' : 'Sms'
);
await client.successSend(patchInteractionIdentifiers, { await client.successSend(patchInteractionIdentifiers, {
...profile, ...profile,
verificationCode, verificationCode,

View file

@ -48,7 +48,7 @@ describe('reset password', () => {
email: userProfile.primaryEmail, email: userProfile.primaryEmail,
}); });
const verificationCodeRecord = await readVerificationCode(); const verificationCodeRecord = await readVerificationCode('Email');
expect(verificationCodeRecord).toMatchObject({ expect(verificationCodeRecord).toMatchObject({
address: userProfile.primaryEmail, address: userProfile.primaryEmail,
@ -108,7 +108,7 @@ describe('reset password', () => {
phone: userProfile.primaryPhone, phone: userProfile.primaryPhone,
}); });
const verificationCodeRecord = await readVerificationCode(); const verificationCodeRecord = await readVerificationCode('Sms');
expect(verificationCodeRecord).toMatchObject({ expect(verificationCodeRecord).toMatchObject({
phone: userProfile.primaryPhone, phone: userProfile.primaryPhone,

View file

@ -31,7 +31,7 @@ describe('reset password flow sad path', () => {
email: primaryEmail, email: primaryEmail,
}); });
const { code: verificationCode } = await readVerificationCode(); const { code: verificationCode } = await readVerificationCode('Email');
await client.successSend(patchInteractionIdentifiers, { await client.successSend(patchInteractionIdentifiers, {
email: primaryEmail, email: primaryEmail,
verificationCode, verificationCode,
@ -59,7 +59,7 @@ describe('reset password flow sad path', () => {
phone: primaryPhone, phone: primaryPhone,
}); });
const { code: verificationCode } = await readVerificationCode(); const { code: verificationCode } = await readVerificationCode('Sms');
await client.successSend(patchInteractionIdentifiers, { await client.successSend(patchInteractionIdentifiers, {
phone: primaryPhone, phone: primaryPhone,
verificationCode, verificationCode,
@ -95,7 +95,7 @@ describe('reset password flow sad path', () => {
phone: primaryPhone, phone: primaryPhone,
}); });
const { code: verificationCode } = await readVerificationCode(); const { code: verificationCode } = await readVerificationCode('Sms');
await client.successSend(patchInteractionIdentifiers, { await client.successSend(patchInteractionIdentifiers, {
phone: primaryPhone, phone: primaryPhone,
verificationCode, verificationCode,

View file

@ -79,7 +79,7 @@ describe('register with passwordless identifier', () => {
email: primaryEmail, email: primaryEmail,
}); });
const verificationCodeRecord = await readVerificationCode(); const verificationCodeRecord = await readVerificationCode('Email');
expect(verificationCodeRecord).toMatchObject({ expect(verificationCodeRecord).toMatchObject({
address: primaryEmail, address: primaryEmail,
@ -125,7 +125,7 @@ describe('register with passwordless identifier', () => {
email: primaryEmail, email: primaryEmail,
}); });
const verificationCodeRecord = await readVerificationCode(); const verificationCodeRecord = await readVerificationCode('Email');
const { code } = verificationCodeRecord; const { code } = verificationCodeRecord;
@ -186,7 +186,7 @@ describe('register with passwordless identifier', () => {
phone: primaryPhone, phone: primaryPhone,
}); });
const verificationCodeRecord = await readVerificationCode(); const verificationCodeRecord = await readVerificationCode('Sms');
expect(verificationCodeRecord).toMatchObject({ expect(verificationCodeRecord).toMatchObject({
phone: primaryPhone, phone: primaryPhone,
@ -232,7 +232,7 @@ describe('register with passwordless identifier', () => {
phone: primaryPhone, phone: primaryPhone,
}); });
const { code } = await readVerificationCode(); const { code } = await readVerificationCode('Sms');
await client.successSend(patchInteractionIdentifiers, { await client.successSend(patchInteractionIdentifiers, {
phone: primaryPhone, phone: primaryPhone,
@ -296,7 +296,7 @@ describe('register with passwordless identifier', () => {
email: primaryEmail, email: primaryEmail,
}); });
const verificationCodeRecord = await readVerificationCode(); const verificationCodeRecord = await readVerificationCode('Email');
expect(verificationCodeRecord).toMatchObject({ expect(verificationCodeRecord).toMatchObject({
address: primaryEmail, address: primaryEmail,
@ -351,7 +351,7 @@ describe('register with passwordless identifier', () => {
phone: primaryPhone, phone: primaryPhone,
}); });
const verificationCodeRecord = await readVerificationCode(); const verificationCodeRecord = await readVerificationCode('Sms');
expect(verificationCodeRecord).toMatchObject({ expect(verificationCodeRecord).toMatchObject({
phone: primaryPhone, phone: primaryPhone,

View file

@ -30,7 +30,7 @@ describe('Register with identifiers sad path', () => {
await clearSsoConnectors(); await clearSsoConnectors();
}); });
it('Should fail to register if sign-in mode is sign-in only', async () => { it('should fail to register if sign-in mode is sign-in only', async () => {
await updateSignInExperience({ signInMode: SignInMode.SignIn }); await updateSignInExperience({ signInMode: SignInMode.SignIn });
const client = await initClient(); const client = await initClient();
@ -48,13 +48,13 @@ describe('Register with identifiers sad path', () => {
await updateSignInExperience({ signInMode: SignInMode.SignInAndRegister }); await updateSignInExperience({ signInMode: SignInMode.SignInAndRegister });
}); });
describe('Should fail to register with identifiers if sign-up settings are not enabled', () => { describe('should fail to register with identifiers if sign-up settings are not enabled', () => {
beforeAll(async () => { beforeAll(async () => {
// This function call will disable all sign-up settings by default // This function call will disable all sign-up settings by default
await enableAllPasswordSignInMethods(); await enableAllPasswordSignInMethods();
}); });
it('Should fail to register with username and password', async () => { it('should fail to register with username and password', async () => {
const client = await initClient(); const client = await initClient();
await expectRejects( await expectRejects(
@ -72,7 +72,7 @@ describe('Register with identifiers sad path', () => {
); );
}); });
it('Should fail to register with email', async () => { it('should fail to register with email', async () => {
await setEmailConnector(); await setEmailConnector();
const { primaryEmail } = generateNewUserProfile({ primaryEmail: true }); const { primaryEmail } = generateNewUserProfile({ primaryEmail: true });
const client = await initClient(); const client = await initClient();
@ -85,7 +85,7 @@ describe('Register with identifiers sad path', () => {
email: primaryEmail, email: primaryEmail,
}); });
const { code: verificationCode } = await readVerificationCode(); const { code: verificationCode } = await readVerificationCode('Email');
await expectRejects( await expectRejects(
client.send(patchInteractionIdentifiers, { client.send(patchInteractionIdentifiers, {
@ -102,7 +102,7 @@ describe('Register with identifiers sad path', () => {
await clearConnectorsByTypes([ConnectorType.Email]); await clearConnectorsByTypes([ConnectorType.Email]);
}); });
it('Should fail to register with phone', async () => { it('should fail to register with phone', async () => {
await setSmsConnector(); await setSmsConnector();
const { primaryPhone } = generateNewUserProfile({ primaryPhone: true }); const { primaryPhone } = generateNewUserProfile({ primaryPhone: true });
@ -116,7 +116,7 @@ describe('Register with identifiers sad path', () => {
phone: primaryPhone, phone: primaryPhone,
}); });
const { code: verificationCode } = await readVerificationCode(); const { code: verificationCode } = await readVerificationCode('Sms');
await expectRejects( await expectRejects(
client.send(patchInteractionIdentifiers, { client.send(patchInteractionIdentifiers, {

View file

@ -32,7 +32,7 @@ const happyPath = async (email: string) => {
email, email,
}); });
const verificationCodeRecord = await readVerificationCode(); const verificationCodeRecord = await readVerificationCode('Email');
expect(verificationCodeRecord).toMatchObject({ expect(verificationCodeRecord).toMatchObject({
address: email, address: email,
@ -124,7 +124,7 @@ describe('test register with email with SSO feature', () => {
email, email,
}); });
const { code: verificationCode } = await readVerificationCode(); const { code: verificationCode } = await readVerificationCode('Email');
await expectRejects( await expectRejects(
client.send(patchInteractionIdentifiers, { client.send(patchInteractionIdentifiers, {

View file

@ -44,7 +44,7 @@ describe('Sign-in flow using verification-code identifiers', () => {
email: userProfile.primaryEmail, email: userProfile.primaryEmail,
}); });
const verificationCodeRecord = await readVerificationCode(); const verificationCodeRecord = await readVerificationCode('Email');
expect(verificationCodeRecord).toMatchObject({ expect(verificationCodeRecord).toMatchObject({
address: userProfile.primaryEmail, address: userProfile.primaryEmail,
@ -77,7 +77,7 @@ describe('Sign-in flow using verification-code identifiers', () => {
phone: userProfile.primaryPhone, phone: userProfile.primaryPhone,
}); });
const verificationCodeRecord = await readVerificationCode(); const verificationCodeRecord = await readVerificationCode('Sms');
expect(verificationCodeRecord).toMatchObject({ expect(verificationCodeRecord).toMatchObject({
phone: userProfile.primaryPhone, phone: userProfile.primaryPhone,
@ -116,7 +116,7 @@ describe('Sign-in flow using verification-code identifiers', () => {
email: newEmail, email: newEmail,
}); });
const verificationCodeRecord = await readVerificationCode(); const verificationCodeRecord = await readVerificationCode('Email');
const { code } = verificationCodeRecord; const { code } = verificationCodeRecord;
@ -158,7 +158,7 @@ describe('Sign-in flow using verification-code identifiers', () => {
phone: newPhone, phone: newPhone,
}); });
const verificationCodeRecord = await readVerificationCode(); const verificationCodeRecord = await readVerificationCode('Sms');
const { code } = verificationCodeRecord; const { code } = verificationCodeRecord;
@ -205,7 +205,7 @@ describe('Sign-in flow using verification-code identifiers', () => {
await client.successSend(sendVerificationCode, { await client.successSend(sendVerificationCode, {
email: userProfile.primaryEmail, email: userProfile.primaryEmail,
}); });
const { code } = await readVerificationCode(); const { code } = await readVerificationCode('Email');
await client.successSend(patchInteractionIdentifiers, { await client.successSend(patchInteractionIdentifiers, {
email: userProfile.primaryEmail, email: userProfile.primaryEmail,
@ -267,7 +267,7 @@ describe('Sign-in flow using verification-code identifiers', () => {
await client.successSend(sendVerificationCode, { await client.successSend(sendVerificationCode, {
email: userProfile.primaryEmail, email: userProfile.primaryEmail,
}); });
const { code } = await readVerificationCode(); const { code } = await readVerificationCode('Email');
await client.successSend(patchInteractionIdentifiers, { await client.successSend(patchInteractionIdentifiers, {
email: userProfile.primaryEmail, email: userProfile.primaryEmail,
@ -324,7 +324,7 @@ describe('Sign-in flow using verification-code identifiers', () => {
await client.successSend(sendVerificationCode, { await client.successSend(sendVerificationCode, {
email: userProfile.primaryEmail, email: userProfile.primaryEmail,
}); });
const { code } = await readVerificationCode(); const { code } = await readVerificationCode('Email');
await client.successSend(patchInteractionIdentifiers, { await client.successSend(patchInteractionIdentifiers, {
email: userProfile.primaryEmail, email: userProfile.primaryEmail,

View file

@ -33,7 +33,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
await clearSsoConnectors(); await clearSsoConnectors();
}); });
it('Should fail to sign in with passcode if sign-in mode is register only', async () => { it('should fail to sign in with passcode if sign-in mode is register only', async () => {
await updateSignInExperience({ signInMode: SignInMode.Register }); await updateSignInExperience({ signInMode: SignInMode.Register });
const client = await initClient(); const client = await initClient();
@ -51,7 +51,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
await enableAllVerificationCodeSignInMethods(); await enableAllVerificationCodeSignInMethods();
}); });
it('Should fail to sign in if related identifiers are not enabled', async () => { it('should fail to sign in if related identifiers are not enabled', async () => {
await updateSignInExperience({ await updateSignInExperience({
signIn: { signIn: {
methods: [], methods: [],
@ -72,7 +72,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
email: primaryEmail, email: primaryEmail,
}); });
const { code: emailVerificationCode } = await readVerificationCode(); const { code: emailVerificationCode } = await readVerificationCode('Email');
await expectRejects( await expectRejects(
client.send(patchInteractionIdentifiers, { client.send(patchInteractionIdentifiers, {
@ -90,7 +90,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
phone: primaryPhone, phone: primaryPhone,
}); });
const { code: phoneVerificationCode } = await readVerificationCode(); const { code: phoneVerificationCode } = await readVerificationCode('Sms');
await expectRejects( await expectRejects(
client.send(patchInteractionIdentifiers, { client.send(patchInteractionIdentifiers, {
@ -107,7 +107,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
await enableAllVerificationCodeSignInMethods(); await enableAllVerificationCodeSignInMethods();
}); });
it('Should fail to update sign in email identifier if verification code is incorrect or mismatch', async () => { it('should fail to update sign in email identifier if verification code is incorrect or mismatch', async () => {
const { const {
userProfile: { primaryEmail }, userProfile: { primaryEmail },
} = await generateNewUser({ primaryEmail: true }); } = await generateNewUser({ primaryEmail: true });
@ -121,7 +121,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
email: primaryEmail, email: primaryEmail,
}); });
const { code: verificationCode } = await readVerificationCode(); const { code: verificationCode } = await readVerificationCode('Email');
await expectRejects( await expectRejects(
client.send(patchInteractionIdentifiers, { client.send(patchInteractionIdentifiers, {
@ -146,7 +146,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
); );
}); });
it('Should fail to update sign in phone identifier if verification code is incorrect or mismatch', async () => { it('should fail to update sign in phone identifier if verification code is incorrect or mismatch', async () => {
const { const {
userProfile: { primaryPhone }, userProfile: { primaryPhone },
} = await generateNewUser({ primaryPhone: true }); } = await generateNewUser({ primaryPhone: true });
@ -160,7 +160,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
phone: primaryPhone, phone: primaryPhone,
}); });
const { code: verificationCode } = await readVerificationCode(); const { code: verificationCode } = await readVerificationCode('Sms');
await expectRejects( await expectRejects(
client.send(patchInteractionIdentifiers, { client.send(patchInteractionIdentifiers, {
@ -185,7 +185,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
); );
}); });
it('Should fail to sign in with email and passcode if related user is not exist', async () => { it('should fail to sign in with email and passcode if related user is not exist', async () => {
const notExistUserEmail = generateEmail(); const notExistUserEmail = generateEmail();
const client = await initClient(); const client = await initClient();
@ -198,7 +198,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
email: notExistUserEmail, email: notExistUserEmail,
}); });
const { code: verificationCode } = await readVerificationCode(); const { code: verificationCode } = await readVerificationCode('Email');
await client.successSend(patchInteractionIdentifiers, { await client.successSend(patchInteractionIdentifiers, {
email: notExistUserEmail, email: notExistUserEmail,
@ -211,7 +211,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
}); });
}); });
it('Should fail to sign in with phone and passcode if related user is not exist', async () => { it('should fail to sign in with phone and passcode if related user is not exist', async () => {
const notExistUserPhone = generatePhone(); const notExistUserPhone = generatePhone();
const client = await initClient(); const client = await initClient();
@ -224,7 +224,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
phone: notExistUserPhone, phone: notExistUserPhone,
}); });
const { code: verificationCode } = await readVerificationCode(); const { code: verificationCode } = await readVerificationCode('Sms');
await client.successSend(patchInteractionIdentifiers, { await client.successSend(patchInteractionIdentifiers, {
phone: notExistUserPhone, phone: notExistUserPhone,
@ -237,7 +237,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
}); });
}); });
it('Should fail to sign in if related user is suspended', async () => { it('should fail to sign in if related user is suspended', async () => {
const { const {
userProfile: { primaryEmail }, userProfile: { primaryEmail },
user: { id: userId }, user: { id: userId },
@ -254,7 +254,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
email: primaryEmail, email: primaryEmail,
}); });
const { code: verificationCode } = await readVerificationCode(); const { code: verificationCode } = await readVerificationCode('Email');
await client.successSend(patchInteractionIdentifiers, { await client.successSend(patchInteractionIdentifiers, {
email: primaryEmail, email: primaryEmail,

View file

@ -32,7 +32,7 @@ const happyPath = async (email: string) => {
email, email,
}); });
const verificationCodeRecord = await readVerificationCode(); const verificationCodeRecord = await readVerificationCode('Email');
expect(verificationCodeRecord).toMatchObject({ expect(verificationCodeRecord).toMatchObject({
address: email, address: email,
@ -70,7 +70,7 @@ describe('test sign-in with email passcode identifier with SSO feature', () => {
await clearSsoConnectors(); await clearSsoConnectors();
}); });
it('Should fail to sign in with email and passcode if the email domain is enabled for SSO only', async () => { it('should fail to sign in with email and passcode if the email domain is enabled for SSO only', async () => {
const email = generateEmail('sso-sad-path.io'); const email = generateEmail('sso-sad-path.io');
const user = await createUser({ primaryEmail: email }); const user = await createUser({ primaryEmail: email });
await updateSignInExperience({ singleSignOnEnabled: true }); await updateSignInExperience({ singleSignOnEnabled: true });
@ -91,7 +91,7 @@ describe('test sign-in with email passcode identifier with SSO feature', () => {
email, email,
}); });
const { code: verificationCode } = await readVerificationCode(); const { code: verificationCode } = await readVerificationCode('Email');
await expectRejects( await expectRejects(
client.send(patchInteractionIdentifiers, { client.send(patchInteractionIdentifiers, {
@ -108,7 +108,7 @@ describe('test sign-in with email passcode identifier with SSO feature', () => {
await deleteSsoConnectorById(id); await deleteSsoConnectorById(id);
}); });
it('Should sign-in with email with SSO disabled', async () => { it('should sign-in with email with SSO disabled', async () => {
await updateSignInExperience({ singleSignOnEnabled: false }); await updateSignInExperience({ singleSignOnEnabled: false });
const { id } = await createSsoConnector({ const { id } = await createSsoConnector({
@ -123,7 +123,7 @@ describe('test sign-in with email passcode identifier with SSO feature', () => {
await deleteSsoConnectorById(id); await deleteSsoConnectorById(id);
}); });
it('Should sign-in with email with SSO enabled but no connector found', async () => { it('should sign-in with email with SSO enabled but no connector found', async () => {
await updateSignInExperience({ singleSignOnEnabled: true }); await updateSignInExperience({ singleSignOnEnabled: true });
const { id } = await createSsoConnector({ const { id } = await createSsoConnector({

View file

@ -121,7 +121,7 @@ describe('Sign-in flow using password identifiers', () => {
email: primaryEmail, email: primaryEmail,
}); });
const { code } = await readVerificationCode(); const { code } = await readVerificationCode('Email');
await client.successSend(patchInteractionIdentifiers, { await client.successSend(patchInteractionIdentifiers, {
email: primaryEmail, email: primaryEmail,
@ -183,7 +183,7 @@ describe('Sign-in flow using password identifiers', () => {
phone: primaryPhone, phone: primaryPhone,
}); });
const { code } = await readVerificationCode(); const { code } = await readVerificationCode('Sms');
await client.successSend(patchInteractionIdentifiers, { await client.successSend(patchInteractionIdentifiers, {
phone: primaryPhone, phone: primaryPhone,

View file

@ -26,7 +26,7 @@ describe('Generic verification code through management API', () => {
}); });
afterEach(async () => { afterEach(async () => {
await removeVerificationCode(); await Promise.all([removeVerificationCode('Sms'), removeVerificationCode('Email')]);
}); });
it('should create an email verification code on server side', async () => { it('should create an email verification code on server side', async () => {
@ -34,7 +34,7 @@ describe('Generic verification code through management API', () => {
const response = await requestVerificationCode(payload); const response = await requestVerificationCode(payload);
expect(response.statusCode).toBe(204); expect(response.statusCode).toBe(204);
const { code, type, address } = await readVerificationCode(); const { code, type, address } = await readVerificationCode('Email');
expect(type).toBe(TemplateType.Generic); expect(type).toBe(TemplateType.Generic);
expect(address).toBe(mockEmail); expect(address).toBe(mockEmail);
@ -46,7 +46,7 @@ describe('Generic verification code through management API', () => {
const response = await requestVerificationCode(payload); const response = await requestVerificationCode(payload);
expect(response.statusCode).toBe(204); expect(response.statusCode).toBe(204);
const { code, type, phone } = await readVerificationCode(); const { code, type, phone } = await readVerificationCode('Sms');
expect(type).toBe(TemplateType.Generic); expect(type).toBe(TemplateType.Generic);
expect(phone).toBe(mockPhone); expect(phone).toBe(mockPhone);
@ -59,7 +59,8 @@ describe('Generic verification code through management API', () => {
statusCode: 400, statusCode: 400,
}); });
await expect(readVerificationCode()).rejects.toThrow(); await expect(readVerificationCode('Email')).rejects.toThrow();
await expect(readVerificationCode('Sms')).rejects.toThrow();
}); });
it('should fail to send a verification code on server side when no email connector has been set', async () => { it('should fail to send a verification code on server side when no email connector has been set', async () => {
@ -113,7 +114,7 @@ describe('Generic verification code through management API', () => {
it('should be able to verify the email verification code', async () => { it('should be able to verify the email verification code', async () => {
await requestVerificationCode({ email: mockEmail }); await requestVerificationCode({ email: mockEmail });
const { code } = await readVerificationCode(); const { code } = await readVerificationCode('Email');
await expect( await expect(
verifyVerificationCode({ email: mockEmail, verificationCode: code }) verifyVerificationCode({ email: mockEmail, verificationCode: code })
@ -123,7 +124,7 @@ describe('Generic verification code through management API', () => {
it('should be able to verify the sms verification code', async () => { it('should be able to verify the sms verification code', async () => {
await requestVerificationCode({ phone: mockPhone }); await requestVerificationCode({ phone: mockPhone });
const { code } = await readVerificationCode(); const { code } = await readVerificationCode('Sms');
await expect( await expect(
verifyVerificationCode({ phone: mockPhone, verificationCode: code }) verifyVerificationCode({ phone: mockPhone, verificationCode: code })
@ -132,7 +133,7 @@ describe('Generic verification code through management API', () => {
it('should throw when the code is not valid', async () => { it('should throw when the code is not valid', async () => {
await requestVerificationCode({ phone: mockPhone }); await requestVerificationCode({ phone: mockPhone });
await readVerificationCode(); await readVerificationCode('Sms');
await expectRejects(verifyVerificationCode({ phone: mockPhone, verificationCode: '666' }), { await expectRejects(verifyVerificationCode({ phone: mockPhone, verificationCode: '666' }), {
code: 'verification_code.code_mismatch', code: 'verification_code.code_mismatch',
statusCode: 400, statusCode: 400,
@ -143,7 +144,7 @@ describe('Generic verification code through management API', () => {
const phoneToVerify = '666'; const phoneToVerify = '666';
const phoneToGetCode = mockPhone; const phoneToGetCode = mockPhone;
await requestVerificationCode({ phone: phoneToGetCode }); await requestVerificationCode({ phone: phoneToGetCode });
const { code, phone } = await readVerificationCode(); const { code, phone } = await readVerificationCode('Sms');
expect(phoneToGetCode).toEqual(phone); expect(phoneToGetCode).toEqual(phone);
await expectRejects(verifyVerificationCode({ phone: phoneToVerify, verificationCode: code }), { await expectRejects(verifyVerificationCode({ phone: phoneToVerify, verificationCode: code }), {
code: 'verification_code.not_found', code: 'verification_code.not_found',
@ -155,7 +156,7 @@ describe('Generic verification code through management API', () => {
const emailToVerify = 'verify_email@mail.com'; const emailToVerify = 'verify_email@mail.com';
const emailToGetCode = mockEmail; const emailToGetCode = mockEmail;
await requestVerificationCode({ email: emailToGetCode }); await requestVerificationCode({ email: emailToGetCode });
const { code, address } = await readVerificationCode(); const { code, address } = await readVerificationCode('Email');
expect(emailToGetCode).toEqual(address); expect(emailToGetCode).toEqual(address);
await expectRejects(verifyVerificationCode({ email: emailToVerify, verificationCode: code }), { await expectRejects(verifyVerificationCode({ email: emailToVerify, verificationCode: code }), {
code: 'verification_code.not_found', code: 'verification_code.not_found',

View file

@ -48,7 +48,7 @@ describe('MFA - TOTP', () => {
const experience = new ExpectTotpExperience(await browser.newPage()); const experience = new ExpectTotpExperience(await browser.newPage());
await experience.startWith(demoAppUrl, 'sign-in'); await experience.startWith(demoAppUrl, 'sign-in');
await experience.toFillInput('identifier', userProfile.primaryEmail, { submit: true }); await experience.toFillInput('identifier', userProfile.primaryEmail, { submit: true });
await experience.toCompleteVerification('sign-in'); await experience.toCompleteVerification('sign-in', 'Email');
// Add missing password // Add missing password
await experience.toFillInput('newPassword', 'l0gt0_T3st_P@ssw0rd', { submit: true }); await experience.toFillInput('newPassword', 'l0gt0_T3st_P@ssw0rd', { submit: true });
@ -99,7 +99,7 @@ describe('MFA - TOTP', () => {
// Add missing phone number // Add missing phone number
await waitFor(500); await waitFor(500);
await experience.toFillInput('identifier', generatePhone(), { submit: true }); await experience.toFillInput('identifier', generatePhone(), { submit: true });
await experience.toCompleteVerification('continue'); await experience.toCompleteVerification('continue', 'Sms');
// Bind TOTP // Bind TOTP
await experience.toBindTotp(); await experience.toBindTotp();
@ -147,7 +147,7 @@ describe('MFA - TOTP', () => {
// Add missing email number // Add missing email number
await waitFor(500); await waitFor(500);
await experience.toFillInput('identifier', generateEmail(), { submit: true }); await experience.toFillInput('identifier', generateEmail(), { submit: true });
await experience.toCompleteVerification('continue'); await experience.toCompleteVerification('continue', 'Email');
// Bind TOTP // Bind TOTP
await experience.toBindTotp(); await experience.toBindTotp();
@ -230,7 +230,7 @@ describe('MFA - TOTP', () => {
// Add missing email // Add missing email
await verificationExperience.toFillInput('identifier', generateEmail(), { submit: true }); await verificationExperience.toFillInput('identifier', generateEmail(), { submit: true });
await verificationExperience.toCompleteVerification('continue'); await verificationExperience.toCompleteVerification('continue', 'Email');
// Wait for the page to load // Wait for the page to load
await waitFor(500); await waitFor(500);
await verificationExperience.verifyThenEnd(); await verificationExperience.verifyThenEnd();

View file

@ -67,7 +67,7 @@ describe('MFA - TOTP', () => {
const experience = new ExpectTotpExperience(await browser.newPage()); const experience = new ExpectTotpExperience(await browser.newPage());
await experience.startWith(demoAppUrl, 'register'); await experience.startWith(demoAppUrl, 'register');
await experience.toFillInput('id', context.userEmail, { submit: true }); await experience.toFillInput('id', context.userEmail, { submit: true });
await experience.toCompleteVerification('register'); await experience.toCompleteVerification('register', 'Email');
context.setUpTotpSecret(await experience.toBindTotp()); context.setUpTotpSecret(await experience.toBindTotp());
await experience.verifyThenEnd(); await experience.verifyThenEnd();
@ -81,7 +81,7 @@ describe('MFA - TOTP', () => {
const experience = new ExpectTotpExperience(await browser.newPage()); const experience = new ExpectTotpExperience(await browser.newPage());
await experience.startWith(demoAppUrl, 'sign-in'); await experience.startWith(demoAppUrl, 'sign-in');
await experience.toFillInput('identifier', context.userEmail, { submit: true }); await experience.toFillInput('identifier', context.userEmail, { submit: true });
await experience.toCompleteVerification('sign-in'); await experience.toCompleteVerification('sign-in', 'Email');
await experience.toVerifyTotp(context.totpSecret); await experience.toVerifyTotp(context.totpSecret);
const userId = await experience.getUserIdFromDemoAppPage(); const userId = await experience.getUserIdFromDemoAppPage();
await experience.verifyThenEnd(); await experience.verifyThenEnd();
@ -95,7 +95,7 @@ describe('MFA - TOTP', () => {
const experience = new ExpectTotpExperience(await browser.newPage()); const experience = new ExpectTotpExperience(await browser.newPage());
await experience.startWith(demoAppUrl, 'sign-in'); await experience.startWith(demoAppUrl, 'sign-in');
await experience.toFillInput('identifier', userProfile.primaryEmail, { submit: true }); await experience.toFillInput('identifier', userProfile.primaryEmail, { submit: true });
await experience.toCompleteVerification('sign-in'); await experience.toCompleteVerification('sign-in', 'Email');
await experience.toBindTotp(); await experience.toBindTotp();
await experience.verifyThenEnd(); await experience.verifyThenEnd();
// Clean up // Clean up
@ -147,7 +147,7 @@ describe('MFA - TOTP', () => {
const experience = new ExpectTotpExperience(await browser.newPage()); const experience = new ExpectTotpExperience(await browser.newPage());
await experience.startWith(demoAppUrl, 'register'); await experience.startWith(demoAppUrl, 'register');
await experience.toFillInput('id', context.userPhone, { submit: true }); await experience.toFillInput('id', context.userPhone, { submit: true });
await experience.toCompleteVerification('register'); await experience.toCompleteVerification('register', 'Sms');
context.setUpTotpSecret(await experience.toBindTotp()); context.setUpTotpSecret(await experience.toBindTotp());
@ -163,7 +163,7 @@ describe('MFA - TOTP', () => {
const experience = new ExpectTotpExperience(await browser.newPage()); const experience = new ExpectTotpExperience(await browser.newPage());
await experience.startWith(demoAppUrl, 'sign-in'); await experience.startWith(demoAppUrl, 'sign-in');
await experience.toFillInput('identifier', context.userPhone, { submit: true }); await experience.toFillInput('identifier', context.userPhone, { submit: true });
await experience.toCompleteVerification('sign-in'); await experience.toCompleteVerification('sign-in', 'Sms');
await experience.toVerifyTotp(context.totpSecret); await experience.toVerifyTotp(context.totpSecret);
const userId = await experience.getUserIdFromDemoAppPage(); const userId = await experience.getUserIdFromDemoAppPage();
await experience.verifyThenEnd(); await experience.verifyThenEnd();
@ -179,7 +179,7 @@ describe('MFA - TOTP', () => {
await experience.toFillInput('identifier', userProfile.primaryPhone.slice(1), { await experience.toFillInput('identifier', userProfile.primaryPhone.slice(1), {
submit: true, submit: true,
}); });
await experience.toCompleteVerification('sign-in'); await experience.toCompleteVerification('sign-in', 'Sms');
await experience.toBindTotp(); await experience.toBindTotp();
await experience.verifyThenEnd(); await experience.verifyThenEnd();

View file

@ -70,7 +70,7 @@ describe('password policy', () => {
// Complete verification code flow // Complete verification code flow
await experience.toFillInput('id', email, { submit: true }); await experience.toFillInput('id', email, { submit: true });
await experience.toCompleteVerification('register'); await experience.toCompleteVerification('register', 'Email');
// Wait for the password page to load // Wait for the password page to load
await waitFor(100); await waitFor(100);
@ -98,7 +98,7 @@ describe('password policy', () => {
await experience.toClickSubmit(); await experience.toClickSubmit();
// Complete verification code flow // Complete verification code flow
await experience.toCompleteVerification('forgot-password'); await experience.toCompleteVerification('forgot-password', 'Email');
// Wait for the password page to load // Wait for the password page to load
await waitFor(100); await waitFor(100);

View file

@ -131,9 +131,12 @@ export default class ExpectExperience extends ExpectPage {
* *
* @param type The type of experience to expect. * @param type The type of experience to expect.
*/ */
async toCompleteVerification(type: ExperienceType) { async toCompleteVerification(
type: ExperienceType,
connectorType: Parameters<typeof readVerificationCode>['0']
) {
this.toBeAt(`${type}/verification-code`); this.toBeAt(`${type}/verification-code`);
const { code } = await readVerificationCode(); const { code } = await readVerificationCode(connectorType);
await this.toFillVerificationCode(code); await this.toFillVerificationCode(code);
} }

View file

@ -5,6 +5,7 @@ import {
ConnectorErrorCodes, ConnectorErrorCodes,
sendMessagePayloadKeys, sendMessagePayloadKeys,
type SendMessagePayload, type SendMessagePayload,
ConnectorType,
} from './types/index.js'; } from './types/index.js';
export * from './types/index.js'; export * from './types/index.js';
@ -42,8 +43,18 @@ export const parseJsonObject = (...args: Parameters<typeof parseJson>) => {
return parsed; return parsed;
}; };
/** @deprecated Use {@link mockConnectorFilePaths} instead. */
export const mockSmsVerificationCodeFileName = 'logto_mock_verification_code_record.txt'; export const mockSmsVerificationCodeFileName = 'logto_mock_verification_code_record.txt';
/**
* The file paths for storing the mock sms/email connector records. You can use these file paths to
* read the records for testing.
*/
export const mockConnectorFilePaths = Object.freeze({
[ConnectorType.Sms]: '/tmp/logto_mock_sms_record.txt',
[ConnectorType.Email]: '/tmp/logto_mock_email_record.txt',
});
/** /**
* Replace all handlebars that match the keys in {@link SendMessagePayload} with the payload * Replace all handlebars that match the keys in {@link SendMessagePayload} with the payload
* values. If the payload does not contain the key, the handlebar will be replaced with an empty * values. If the payload does not contain the key, the handlebar will be replaced with an empty