mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
refactor: use ky in integration tests (#5584)
* refactor: use ky in integration tests * refactor: remove node-fetch * refactor: fix test cases * refactor: remove waitFor after each test
This commit is contained in:
parent
eb4c6c4e59
commit
6d56434b48
85 changed files with 556 additions and 582 deletions
|
@ -84,7 +84,7 @@
|
|||
"jest-transform-stub": "^2.0.0",
|
||||
"jest-transformer-svg": "^2.0.0",
|
||||
"just-kebab-case": "^4.2.0",
|
||||
"ky": "^1.0.0",
|
||||
"ky": "^1.2.3",
|
||||
"libphonenumber-js": "^1.10.51",
|
||||
"lint-staged": "^15.0.0",
|
||||
"nanoid": "^5.0.1",
|
||||
|
|
|
@ -67,7 +67,7 @@
|
|||
"jest-transform-stub": "^2.0.0",
|
||||
"jest-transformer-svg": "^2.0.0",
|
||||
"js-base64": "^3.7.5",
|
||||
"ky": "^1.0.0",
|
||||
"ky": "^1.2.3",
|
||||
"libphonenumber-js": "^1.10.51",
|
||||
"lint-staged": "^15.0.0",
|
||||
"parcel": "2.9.3",
|
||||
|
|
|
@ -5,13 +5,3 @@ import { authedAdminTenantApi } from './lib/api/api.js';
|
|||
await authedAdminTenantApi.patch('sign-in-exp', {
|
||||
json: { signInMode: 'SignInAndRegister' },
|
||||
});
|
||||
|
||||
const waitFor = async (ms) =>
|
||||
new Promise((resolve) => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
|
||||
global.afterEach(async () => {
|
||||
// Try to mitigate the issue of "Socket hang up". See https://github.com/nodejs/node/issues/47130
|
||||
await waitFor(1);
|
||||
});
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import dotenv from 'dotenv';
|
||||
import { setDefaultOptions } from 'expect-puppeteer';
|
||||
import fetch from 'node-fetch';
|
||||
import { TextDecoder, TextEncoder } from 'text-encoder';
|
||||
|
||||
const { jest } = import.meta;
|
||||
|
@ -8,9 +7,12 @@ const { jest } = import.meta;
|
|||
dotenv.config();
|
||||
|
||||
/* eslint-disable @silverhand/fp/no-mutation */
|
||||
global.fetch = fetch;
|
||||
global.TextDecoder = TextDecoder;
|
||||
global.TextEncoder = TextEncoder;
|
||||
global.fail = (message) => {
|
||||
throw new Error(message);
|
||||
};
|
||||
|
||||
/* eslint-enable @silverhand/fp/no-mutation */
|
||||
|
||||
// GitHub Actions default runners need more time for UI tests
|
||||
|
|
|
@ -38,12 +38,11 @@
|
|||
"dotenv": "^16.0.0",
|
||||
"eslint": "^8.44.0",
|
||||
"expect-puppeteer": "^10.0.0",
|
||||
"got": "^14.0.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-matcher-specific-error": "^1.0.0",
|
||||
"jest-puppeteer": "^10.0.1",
|
||||
"jose": "^5.0.0",
|
||||
"node-fetch": "^3.3.0",
|
||||
"ky": "^1.2.3",
|
||||
"openapi-schema-validator": "^12.1.3",
|
||||
"openapi-types": "^12.1.3",
|
||||
"prettier": "^3.0.0",
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { appendPath } from '@silverhand/essentials';
|
||||
import { got } from 'got';
|
||||
import ky from 'ky';
|
||||
|
||||
import { logtoConsoleUrl, logtoUrl, logtoCloudUrl } from '#src/constants.js';
|
||||
|
||||
const api = got.extend({
|
||||
const api = ky.extend({
|
||||
prefixUrl: appendPath(new URL(logtoUrl), 'api'),
|
||||
});
|
||||
|
||||
|
@ -16,7 +16,7 @@ export const authedAdminApi = api.extend({
|
|||
},
|
||||
});
|
||||
|
||||
export const adminTenantApi = got.extend({
|
||||
export const adminTenantApi = ky.extend({
|
||||
prefixUrl: appendPath(new URL(logtoConsoleUrl), 'api'),
|
||||
});
|
||||
|
||||
|
@ -26,10 +26,10 @@ export const authedAdminTenantApi = adminTenantApi.extend({
|
|||
},
|
||||
});
|
||||
|
||||
export const cloudApi = got.extend({
|
||||
export const cloudApi = ky.extend({
|
||||
prefixUrl: appendPath(new URL(logtoCloudUrl), 'api'),
|
||||
});
|
||||
|
||||
export const oidcApi = got.extend({
|
||||
export const oidcApi = ky.extend({
|
||||
prefixUrl: appendPath(new URL(logtoUrl), 'oidc'),
|
||||
});
|
||||
|
|
|
@ -99,10 +99,13 @@ export const generateM2mLog = async (applicationId: string) => {
|
|||
|
||||
// This is a token request with insufficient parameters and should fail. We make the request to generate a log for the current machine to machine app.
|
||||
return oidcApi.post('token', {
|
||||
form: {
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: new URLSearchParams({
|
||||
client_id: id,
|
||||
client_secret: secret,
|
||||
grant_type: 'client_credentials',
|
||||
},
|
||||
}),
|
||||
});
|
||||
};
|
||||
|
|
|
@ -31,14 +31,13 @@ export const postConnector = async (
|
|||
payload: Pick<CreateConnector, 'connectorId' | 'config' | 'metadata' | 'syncProfile'>
|
||||
) =>
|
||||
authedAdminApi
|
||||
.post({
|
||||
url: `connectors`,
|
||||
.post('connectors', {
|
||||
json: payload,
|
||||
})
|
||||
.json<Connector>();
|
||||
|
||||
export const deleteConnectorById = async (id: string) =>
|
||||
authedAdminApi.delete({ url: `connectors/${id}` }).json();
|
||||
authedAdminApi.delete(`connectors/${id}`).json();
|
||||
|
||||
export const updateConnectorConfig = async (
|
||||
id: string,
|
||||
|
@ -46,8 +45,7 @@ export const updateConnectorConfig = async (
|
|||
metadata?: Record<string, unknown>
|
||||
) =>
|
||||
authedAdminApi
|
||||
.patch({
|
||||
url: `connectors/${id}`,
|
||||
.patch(`connectors/${id}`, {
|
||||
json: { config, metadata },
|
||||
})
|
||||
.json<ConnectorResponse>();
|
||||
|
@ -70,8 +68,7 @@ const sendTestMessage = async (
|
|||
receiver: string,
|
||||
config: Record<string, unknown>
|
||||
) =>
|
||||
authedAdminApi.post({
|
||||
url: `connectors/${connectorFactoryId}/test`,
|
||||
authedAdminApi.post(`connectors/${connectorFactoryId}/test`, {
|
||||
json: { [receiverType]: receiver, config },
|
||||
});
|
||||
|
||||
|
@ -81,8 +78,7 @@ export const getConnectorAuthorizationUri = async (
|
|||
redirectUri: string
|
||||
) =>
|
||||
authedAdminApi
|
||||
.post({
|
||||
url: `connectors/${connectorId}/authorization-uri`,
|
||||
.post(`connectors/${connectorId}/authorization-uri`, {
|
||||
json: { state, redirectUri },
|
||||
})
|
||||
.json<{ redirectTo: string }>();
|
||||
|
|
|
@ -9,9 +9,7 @@ export const getCustomPhrase = async (languageTag: string) =>
|
|||
authedAdminApi.get(`custom-phrases/${languageTag}`).json<CustomPhrase>();
|
||||
|
||||
export const createOrUpdateCustomPhrase = async (languageTag: string, translation: Translation) =>
|
||||
authedAdminApi
|
||||
.put({ url: `custom-phrases/${languageTag}`, json: translation })
|
||||
.json<CustomPhrase>();
|
||||
authedAdminApi.put(`custom-phrases/${languageTag}`, { json: translation }).json<CustomPhrase>();
|
||||
|
||||
export const deleteCustomPhrase = async (languageTag: string) =>
|
||||
authedAdminApi.delete(`custom-phrases/${languageTag}`).json();
|
||||
|
|
|
@ -1,5 +1,23 @@
|
|||
import { authedAdminApi } from './api.js';
|
||||
|
||||
/**
|
||||
* Transform the data to a new object or array without modifying the original data.
|
||||
* This is useful since `.json()` returns an object that contains something which makes
|
||||
* it impossible to use `.toStrictEqual()` directly.
|
||||
*/
|
||||
const transform = <T>(data: T): T => {
|
||||
if (Array.isArray(data)) {
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
return [...data] as T;
|
||||
}
|
||||
|
||||
if (typeof data === 'object') {
|
||||
return { ...data };
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
export class ApiFactory<
|
||||
Schema extends Record<string, unknown>,
|
||||
PostData extends Record<string, unknown>,
|
||||
|
@ -8,19 +26,23 @@ export class ApiFactory<
|
|||
constructor(public readonly path: string) {}
|
||||
|
||||
async create(data: PostData): Promise<Schema> {
|
||||
return authedAdminApi.post(this.path, { json: data }).json<Schema>();
|
||||
return transform(await authedAdminApi.post(this.path, { json: data }).json<Schema>());
|
||||
}
|
||||
|
||||
async getList(params?: URLSearchParams): Promise<Schema[]> {
|
||||
return authedAdminApi.get(this.path + '?' + (params?.toString() ?? '')).json<Schema[]>();
|
||||
return transform(
|
||||
await authedAdminApi.get(this.path + '?' + (params?.toString() ?? '')).json<Schema[]>()
|
||||
);
|
||||
}
|
||||
|
||||
async get(id: string): Promise<Schema> {
|
||||
return authedAdminApi.get(this.path + '/' + id).json<Schema>();
|
||||
return transform(await authedAdminApi.get(this.path + '/' + id).json<Schema>());
|
||||
}
|
||||
|
||||
async update(id: string, data: PatchData): Promise<Schema> {
|
||||
return authedAdminApi.patch(this.path + '/' + id, { json: data }).json<Schema>();
|
||||
return transform(
|
||||
await authedAdminApi.patch(this.path + '/' + id, { json: data }).json<Schema>()
|
||||
);
|
||||
}
|
||||
|
||||
async delete(id: string): Promise<void> {
|
||||
|
|
|
@ -15,7 +15,6 @@ export const getSsoAuthorizationUrl = async (
|
|||
.post(`interaction/${ssoPath}/${connectorId}/authorization-url`, {
|
||||
headers: { cookie },
|
||||
json: payload,
|
||||
followRedirect: false,
|
||||
})
|
||||
.json<{ redirectTo: string }>();
|
||||
};
|
||||
|
|
|
@ -7,7 +7,7 @@ import type {
|
|||
VerifyMfaPayload,
|
||||
ConsentInfoResponse,
|
||||
} from '@logto/schemas';
|
||||
import type { Got } from 'got';
|
||||
import { type KyInstance } from 'ky';
|
||||
|
||||
import api from './api.js';
|
||||
|
||||
|
@ -26,7 +26,6 @@ export const putInteraction = async (cookie: string, payload: InteractionPayload
|
|||
.put('interaction', {
|
||||
headers: { cookie },
|
||||
json: payload,
|
||||
followRedirect: false,
|
||||
})
|
||||
.json();
|
||||
|
||||
|
@ -34,21 +33,17 @@ export const deleteInteraction = async (cookie: string) =>
|
|||
api
|
||||
.delete('interaction', {
|
||||
headers: { cookie },
|
||||
followRedirect: false,
|
||||
})
|
||||
.json();
|
||||
|
||||
export const putInteractionEvent = async (cookie: string, payload: { event: InteractionEvent }) =>
|
||||
api
|
||||
.put('interaction/event', { headers: { cookie }, json: payload, followRedirect: false })
|
||||
.json();
|
||||
api.put('interaction/event', { headers: { cookie }, json: payload, redirect: 'manual' }).json();
|
||||
|
||||
export const patchInteractionIdentifiers = async (cookie: string, payload: IdentifierPayload) =>
|
||||
api
|
||||
.patch('interaction/identifiers', {
|
||||
headers: { cookie },
|
||||
json: payload,
|
||||
followRedirect: false,
|
||||
})
|
||||
.json();
|
||||
|
||||
|
@ -57,7 +52,6 @@ export const patchInteractionProfile = async (cookie: string, payload: Profile)
|
|||
.patch('interaction/profile', {
|
||||
headers: { cookie },
|
||||
json: payload,
|
||||
followRedirect: false,
|
||||
})
|
||||
.json();
|
||||
|
||||
|
@ -66,7 +60,6 @@ export const putInteractionProfile = async (cookie: string, payload: Profile) =>
|
|||
.put('interaction/profile', {
|
||||
headers: { cookie },
|
||||
json: payload,
|
||||
followRedirect: false,
|
||||
})
|
||||
.json();
|
||||
|
||||
|
@ -75,7 +68,6 @@ export const postInteractionBindMfa = async (cookie: string, payload: BindMfaPay
|
|||
.post('interaction/bind-mfa', {
|
||||
headers: { cookie },
|
||||
json: payload,
|
||||
followRedirect: false,
|
||||
})
|
||||
.json();
|
||||
|
||||
|
@ -84,7 +76,6 @@ export const putInteractionMfa = async (cookie: string, payload: VerifyMfaPayloa
|
|||
.put('interaction/mfa', {
|
||||
headers: { cookie },
|
||||
json: payload,
|
||||
followRedirect: false,
|
||||
})
|
||||
.json();
|
||||
|
||||
|
@ -92,13 +83,12 @@ export const deleteInteractionProfile = async (cookie: string) =>
|
|||
api
|
||||
.delete('interaction/profile', {
|
||||
headers: { cookie },
|
||||
followRedirect: false,
|
||||
})
|
||||
.json();
|
||||
|
||||
export const submitInteraction = async (api: Got, cookie: string) =>
|
||||
export const submitInteraction = async (api: KyInstance, cookie: string) =>
|
||||
api
|
||||
.post('interaction/submit', { headers: { cookie }, followRedirect: false })
|
||||
.post('interaction/submit', { headers: { cookie }, redirect: 'manual' })
|
||||
.json<RedirectResponse>();
|
||||
|
||||
export const sendVerificationCode = async (
|
||||
|
@ -108,7 +98,6 @@ export const sendVerificationCode = async (
|
|||
api.post('interaction/verification/verification-code', {
|
||||
headers: { cookie },
|
||||
json: payload,
|
||||
followRedirect: false,
|
||||
});
|
||||
|
||||
export type SocialAuthorizationUriPayload = {
|
||||
|
@ -124,7 +113,6 @@ export const createSocialAuthorizationUri = async (
|
|||
api.post('interaction/verification/social-authorization-uri', {
|
||||
headers: { cookie },
|
||||
json: payload,
|
||||
followRedirect: false,
|
||||
});
|
||||
|
||||
export const initTotp = async (cookie: string) =>
|
||||
|
@ -132,7 +120,8 @@ export const initTotp = async (cookie: string) =>
|
|||
.post('interaction/verification/totp', {
|
||||
headers: { cookie },
|
||||
json: {},
|
||||
followRedirect: false,
|
||||
redirect: 'manual',
|
||||
throwHttpErrors: false,
|
||||
})
|
||||
.json<{ secret: string }>();
|
||||
|
||||
|
@ -142,16 +131,16 @@ export const skipMfaBinding = async (cookie: string) =>
|
|||
json: {
|
||||
mfaSkipped: true,
|
||||
},
|
||||
followRedirect: false,
|
||||
});
|
||||
|
||||
export const consent = async (api: Got, cookie: string) =>
|
||||
export const consent = async (api: KyInstance, cookie: string) =>
|
||||
api
|
||||
.post('interaction/consent', {
|
||||
headers: {
|
||||
cookie,
|
||||
},
|
||||
followRedirect: false,
|
||||
redirect: 'manual',
|
||||
throwHttpErrors: false,
|
||||
})
|
||||
.json<RedirectResponse>();
|
||||
|
||||
|
@ -159,7 +148,6 @@ export const getConsentInfo = async (cookie: string) =>
|
|||
api
|
||||
.get('interaction/consent', {
|
||||
headers: { cookie },
|
||||
followRedirect: false,
|
||||
})
|
||||
.json<ConsentInfoResponse>();
|
||||
|
||||
|
@ -170,5 +158,4 @@ export const createSingleSignOnAuthorizationUri = async (
|
|||
api.post('interaction/verification/sso-authorization-uri', {
|
||||
headers: { cookie },
|
||||
json: payload,
|
||||
followRedirect: false,
|
||||
});
|
||||
|
|
|
@ -37,7 +37,7 @@ export class OrganizationApi extends ApiFactory<
|
|||
query?: Query
|
||||
): Promise<[rows: UserWithOrganizationRoles[], totalCount: number]> {
|
||||
const got = await authedAdminApi.get(`${this.path}/${id}/users`, { searchParams: query });
|
||||
return [JSON.parse(got.body), Number(got.headers['total-number'] ?? 0)];
|
||||
return [await got.json(), Number(got.headers.get('total-number') ?? 0)];
|
||||
}
|
||||
|
||||
async deleteUser(id: string, userId: string): Promise<void> {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { Resource, CreateResource } from '@logto/schemas';
|
||||
import type { OptionsOfTextResponseBody } from 'got';
|
||||
import { type Options } from 'ky';
|
||||
|
||||
import { generateResourceIndicator, generateResourceName } from '#src/utils.js';
|
||||
|
||||
|
@ -17,7 +17,7 @@ export const createResource = async (name?: string, indicator?: string) =>
|
|||
|
||||
export const getResources = async () => authedAdminApi.get('resources').json<Resource[]>();
|
||||
|
||||
export const getResource = async (resourceId: string, options?: OptionsOfTextResponseBody) =>
|
||||
export const getResource = async (resourceId: string, options?: Options) =>
|
||||
authedAdminApi.get(`resources/${resourceId}`, options).json<Resource>();
|
||||
|
||||
export const updateResource = async (
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import type { Scope, CreateScope } from '@logto/schemas';
|
||||
import type { OptionsOfTextResponseBody } from 'got';
|
||||
import { type Options } from 'ky';
|
||||
|
||||
import { generateScopeName } from '#src/utils.js';
|
||||
|
||||
import { authedAdminApi } from './api.js';
|
||||
|
||||
export const getScopes = async (resourceId: string, options?: OptionsOfTextResponseBody) =>
|
||||
export const getScopes = async (resourceId: string, options?: Options) =>
|
||||
authedAdminApi.get(`resources/${resourceId}/scopes`, options).json<Scope[]>();
|
||||
|
||||
export const createScope = async (resourceId: string, name?: string) => {
|
||||
|
|
|
@ -3,8 +3,7 @@ import LogtoClient from '@logto/node';
|
|||
import { demoAppApplicationId } from '@logto/schemas';
|
||||
import type { Nullable, Optional } from '@silverhand/essentials';
|
||||
import { assert } from '@silverhand/essentials';
|
||||
import type { Got } from 'got';
|
||||
import { got } from 'got';
|
||||
import ky, { type KyInstance } from 'ky';
|
||||
|
||||
import { submitInteraction } from '#src/api/index.js';
|
||||
import { demoAppRedirectUri, logtoUrl } from '#src/constants.js';
|
||||
|
@ -23,12 +22,12 @@ export default class MockClient {
|
|||
protected readonly logto: LogtoClient;
|
||||
|
||||
private navigateUrl?: string;
|
||||
private readonly api: Got;
|
||||
private readonly api: KyInstance;
|
||||
|
||||
constructor(config?: Partial<LogtoConfig>) {
|
||||
this.storage = new MemoryStorage();
|
||||
this.config = { ...defaultConfig, ...config };
|
||||
this.api = got.extend({ prefixUrl: this.config.endpoint + '/api' });
|
||||
this.api = ky.extend({ prefixUrl: this.config.endpoint + '/api' });
|
||||
|
||||
this.logto = new LogtoClient(this.config, {
|
||||
navigate: (url: string) => {
|
||||
|
@ -72,19 +71,22 @@ export default class MockClient {
|
|||
);
|
||||
|
||||
// Mock SDK sign-in navigation
|
||||
const response = await got(this.navigateUrl, {
|
||||
followRedirect: false,
|
||||
const response = await ky(this.navigateUrl, {
|
||||
redirect: 'manual',
|
||||
throwHttpErrors: false,
|
||||
});
|
||||
|
||||
// Note: should redirect to sign-in page
|
||||
assert(
|
||||
response.statusCode === 303 &&
|
||||
response.headers.location?.startsWith(options.directSignIn ? '/direct/' : '/sign-in'),
|
||||
response.status === 303 &&
|
||||
response.headers
|
||||
.get('location')
|
||||
?.startsWith(options.directSignIn ? '/direct/' : '/sign-in'),
|
||||
new Error('Visit sign in uri failed')
|
||||
);
|
||||
|
||||
// Get session cookie
|
||||
this.rawCookies = response.headers['set-cookie'] ?? [];
|
||||
this.rawCookies = response.headers.getSetCookie();
|
||||
assert(this.interactionCookie, new Error('Get cookie from authorization endpoint failed'));
|
||||
}
|
||||
|
||||
|
@ -100,20 +102,21 @@ export default class MockClient {
|
|||
new Error('SignIn or Register failed')
|
||||
);
|
||||
|
||||
const authResponse = await got.get(redirectTo, {
|
||||
const authResponse = await ky.get(redirectTo, {
|
||||
headers: {
|
||||
cookie: this.interactionCookie,
|
||||
},
|
||||
followRedirect: false,
|
||||
redirect: 'manual',
|
||||
throwHttpErrors: false,
|
||||
});
|
||||
|
||||
// Note: Should redirect to logto consent page
|
||||
assert(
|
||||
authResponse.statusCode === 303 && authResponse.headers.location === '/consent',
|
||||
authResponse.status === 303 && authResponse.headers.get('location') === '/consent',
|
||||
new Error('Invoke auth before consent failed')
|
||||
);
|
||||
|
||||
this.rawCookies = authResponse.headers['set-cookie'] ?? [];
|
||||
this.rawCookies = authResponse.headers.getSetCookie();
|
||||
|
||||
// Manually handle the consent flow
|
||||
if (!consent) {
|
||||
|
@ -138,7 +141,7 @@ export default class MockClient {
|
|||
}
|
||||
|
||||
await this.logto.signOut(postSignOutRedirectUri);
|
||||
await got(this.navigateUrl);
|
||||
await ky(this.navigateUrl);
|
||||
}
|
||||
|
||||
public async isAuthenticated() {
|
||||
|
@ -176,30 +179,32 @@ export default class MockClient {
|
|||
assert(this.interactionCookie, new Error('Session not found'));
|
||||
assert(this.interactionCookie.includes('_session.sig'), new Error('Session not found'));
|
||||
|
||||
const consentResponse = await got.get(`${this.config.endpoint}/consent`, {
|
||||
const consentResponse = await ky.get(`${this.config.endpoint}/consent`, {
|
||||
headers: {
|
||||
cookie: this.interactionCookie,
|
||||
},
|
||||
followRedirect: false,
|
||||
redirect: 'manual',
|
||||
throwHttpErrors: false,
|
||||
});
|
||||
|
||||
// Consent page should auto consent and redirect to auth endpoint
|
||||
const redirectTo = consentResponse.headers.location;
|
||||
const redirectTo = consentResponse.headers.get('location');
|
||||
|
||||
if (!redirectTo?.startsWith(`${this.config.endpoint}/oidc/auth`)) {
|
||||
throw new Error('Consent failed');
|
||||
}
|
||||
|
||||
const authCodeResponse = await got.get(redirectTo, {
|
||||
const authCodeResponse = await ky.get(redirectTo, {
|
||||
headers: {
|
||||
cookie: this.interactionCookie,
|
||||
},
|
||||
followRedirect: false,
|
||||
redirect: 'manual',
|
||||
throwHttpErrors: false,
|
||||
});
|
||||
|
||||
// Note: Should redirect to the signInCallbackUri
|
||||
assert(authCodeResponse.statusCode === 303, new Error('Complete auth failed'));
|
||||
const signInCallbackUri = authCodeResponse.headers.location;
|
||||
assert(authCodeResponse.status === 303, new Error('Complete auth failed'));
|
||||
const signInCallbackUri = authCodeResponse.headers.get('location');
|
||||
assert(signInCallbackUri, new Error('Get sign in callback uri failed'));
|
||||
|
||||
return signInCallbackUri;
|
||||
|
|
|
@ -62,7 +62,8 @@ export const putInteraction = async (cookie: string, payload: InteractionPayload
|
|||
.put('interaction', {
|
||||
headers: { cookie },
|
||||
json: payload,
|
||||
followRedirect: false,
|
||||
redirect: 'manual',
|
||||
throwHttpErrors: false,
|
||||
})
|
||||
.json();
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
type JsonObject,
|
||||
type UsersPasswordEncryptionMethod,
|
||||
} from '@logto/schemas';
|
||||
import { RequestError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import { createUser } from '#src/api/index.js';
|
||||
import { generateUsername } from '#src/utils.js';
|
||||
|
@ -77,7 +77,7 @@ export const removeConnectorMessage = async (
|
|||
|
||||
type ExpectedErrorInfo = {
|
||||
code: string;
|
||||
statusCode: number;
|
||||
status: number;
|
||||
messageIncludes?: string;
|
||||
};
|
||||
|
||||
|
@ -94,16 +94,16 @@ export const expectRejects = async <T = void>(
|
|||
fail();
|
||||
};
|
||||
|
||||
export const expectRequestError = <T = void>(error: unknown, expected: ExpectedErrorInfo) => {
|
||||
const { code, statusCode, messageIncludes } = expected;
|
||||
const expectRequestError = async <T = void>(error: unknown, expected: ExpectedErrorInfo) => {
|
||||
const { code, status, messageIncludes } = expected;
|
||||
|
||||
if (!(error instanceof RequestError)) {
|
||||
if (!(error instanceof HTTPError)) {
|
||||
fail('Error should be an instance of RequestError');
|
||||
}
|
||||
|
||||
// JSON.parse returns `any`. Directly use `as` since we've already know the response body structure.
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
const body = JSON.parse(String(error.response?.body)) as {
|
||||
const body = (await error.response.json()) as {
|
||||
code: string;
|
||||
message: string;
|
||||
data: T;
|
||||
|
@ -111,7 +111,7 @@ export const expectRequestError = <T = void>(error: unknown, expected: ExpectedE
|
|||
|
||||
expect(body.code).toEqual(code);
|
||||
|
||||
expect(error.response?.statusCode).toEqual(statusCode);
|
||||
expect(error.response.status).toEqual(status);
|
||||
|
||||
if (messageIncludes) {
|
||||
expect(body.message.includes(messageIncludes)).toBeTruthy();
|
||||
|
|
|
@ -80,7 +80,7 @@ export const createNewSocialUserWithUsernameAndPassword = async (connectorId: st
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.identity_not_exist',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
await client.successSend(patchInteractionIdentifiers, { username, password });
|
||||
await client.successSend(putInteractionProfile, { connectorId });
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
declare module 'node-fetch' {
|
||||
const nodeFetch: typeof fetch;
|
||||
export = nodeFetch;
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import { RoleType } from '@logto/schemas';
|
||||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import { assignRolesToUser, getUserRoles, deleteRoleFromUser } from '#src/api/index.js';
|
||||
import { createRole } from '#src/api/role.js';
|
||||
|
@ -21,7 +21,7 @@ describe('admin console user management (roles)', () => {
|
|||
const m2mRole = await createRole({ type: RoleType.MachineToMachine });
|
||||
await expectRejects(assignRolesToUser(user.id, [m2mRole.id]), {
|
||||
code: 'user.invalid_role_type',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await assignRolesToUser(user.id, [role1.id, role2.id]);
|
||||
|
@ -65,6 +65,6 @@ describe('admin console user management (roles)', () => {
|
|||
const role = await createRole({});
|
||||
|
||||
const response = await deleteRoleFromUser(user.id, role.id).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode === 404).toBe(true);
|
||||
expect(response instanceof HTTPError && response.response.status === 404).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import type { IncomingHttpHeaders } from 'node:http';
|
||||
|
||||
import type { Role, User } from '@logto/schemas';
|
||||
|
||||
import { assignRolesToUser, authedAdminApi, createUser, deleteUser } from '#src/api/index.js';
|
||||
|
@ -10,12 +8,12 @@ import { UserApiTest } from '#src/helpers/user.js';
|
|||
|
||||
const getUsers = async <T>(
|
||||
init: string[][] | Record<string, string> | URLSearchParams
|
||||
): Promise<{ headers: IncomingHttpHeaders; json: T }> => {
|
||||
const { headers, body } = await authedAdminApi.get('users', {
|
||||
): Promise<{ headers: Headers; json: T }> => {
|
||||
const response = await authedAdminApi.get('users', {
|
||||
searchParams: new URLSearchParams(init),
|
||||
});
|
||||
|
||||
return { headers, json: JSON.parse(body) as T };
|
||||
return { headers: response.headers, json: (await response.json()) as T };
|
||||
};
|
||||
|
||||
describe('admin console user search params', () => {
|
||||
|
@ -64,21 +62,21 @@ describe('admin console user search params', () => {
|
|||
it('should return all users if nothing specified', async () => {
|
||||
const { headers } = await getUsers<User[]>([]);
|
||||
|
||||
expect(Number(headers['total-number'])).toBeGreaterThanOrEqual(10);
|
||||
expect(Number(headers.get('total-number'))).toBeGreaterThanOrEqual(10);
|
||||
});
|
||||
|
||||
describe('falling back to `like` mode and matches all available fields if only `search` is specified', () => {
|
||||
it('should search username', async () => {
|
||||
const { headers, json } = await getUsers<User[]>([['search', '%search_tom%']]);
|
||||
|
||||
expect(headers['total-number']).toEqual('5');
|
||||
expect(headers.get('total-number')).toEqual('5');
|
||||
expect(json.length === 5 && json.every((user) => user.name === 'Tom Scott')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should search primaryPhone', async () => {
|
||||
const { headers, json } = await getUsers<User[]>([['search', '%0000%']]);
|
||||
|
||||
expect(headers['total-number']).toEqual('10');
|
||||
expect(headers.get('total-number')).toEqual('10');
|
||||
expect(
|
||||
json.length === 10 && json.every((user) => user.username?.startsWith('search_'))
|
||||
).toBeTruthy();
|
||||
|
@ -92,7 +90,7 @@ describe('admin console user search params', () => {
|
|||
['isCaseSensitive', 'true'],
|
||||
]);
|
||||
|
||||
expect(headers['total-number']).toEqual('0');
|
||||
expect(headers.get('total-number')).toEqual('0');
|
||||
expect(json.length === 0).toBeTruthy();
|
||||
});
|
||||
|
||||
|
@ -102,7 +100,7 @@ describe('admin console user search params', () => {
|
|||
['mode.name', 'exact'],
|
||||
]);
|
||||
|
||||
expect(headers['total-number']).toEqual('2');
|
||||
expect(headers.get('total-number')).toEqual('2');
|
||||
expect(json.length === 2 && json.every((user) => user.name === 'Jerry Swift')).toBeTruthy();
|
||||
});
|
||||
|
||||
|
@ -117,7 +115,7 @@ describe('admin console user search params', () => {
|
|||
['isCaseSensitive', 'true'],
|
||||
]);
|
||||
|
||||
expect(headers['total-number']).toEqual('2');
|
||||
expect(headers.get('total-number')).toEqual('2');
|
||||
expect(json.length === 2 && json.every((user) => user.name === 'Jerry Swift Jr')).toBeTruthy();
|
||||
});
|
||||
|
||||
|
@ -130,7 +128,7 @@ describe('admin console user search params', () => {
|
|||
['isCaseSensitive', 'true'],
|
||||
]);
|
||||
|
||||
expect(headers['total-number']).toEqual('5');
|
||||
expect(headers.get('total-number')).toEqual('5');
|
||||
expect(
|
||||
json.length === 5 && json.every((user) => user.username?.startsWith('search_'))
|
||||
).toBeTruthy();
|
||||
|
@ -144,7 +142,7 @@ describe('admin console user search params', () => {
|
|||
['mode.primaryEmail', 'exact'],
|
||||
]);
|
||||
|
||||
expect(headers['total-number']).toEqual('3');
|
||||
expect(headers.get('total-number')).toEqual('3');
|
||||
expect(
|
||||
json.length === 3 && json.every((user) => user.name?.startsWith('Jerry Swift Jr'))
|
||||
).toBeTruthy();
|
||||
|
@ -161,7 +159,7 @@ describe('admin console user search params', () => {
|
|||
['isCaseSensitive', 'true'],
|
||||
]);
|
||||
|
||||
expect(headers['total-number']).toEqual('3');
|
||||
expect(headers.get('total-number')).toEqual('3');
|
||||
expect(
|
||||
json.length === 3 && json.every((user) => user.username?.startsWith('search_'))
|
||||
).toBeTruthy();
|
||||
|
@ -174,7 +172,7 @@ describe('admin console user search params', () => {
|
|||
['search.primaryEmail', 'jerry_swift_jr_2@geek.best'],
|
||||
['search.primaryEmail', 'jerry_swift_jr_jr@gmail.com'],
|
||||
]),
|
||||
{ code: 'request.invalid_input', statusCode: 400, messageIncludes: '`exact`' }
|
||||
{ code: 'request.invalid_input', status: 400, messageIncludes: '`exact`' }
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -186,7 +184,7 @@ describe('admin console user search params', () => {
|
|||
]),
|
||||
{
|
||||
code: 'request.invalid_input',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
messageIncludes: 'cannot be empty',
|
||||
}
|
||||
);
|
||||
|
@ -200,7 +198,7 @@ describe('admin console user search params', () => {
|
|||
]),
|
||||
{
|
||||
code: 'request.invalid_input',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
messageIncludes: 'case-insensitive',
|
||||
}
|
||||
);
|
||||
|
@ -215,13 +213,13 @@ describe('admin console user search params', () => {
|
|||
]),
|
||||
{
|
||||
code: 'request.invalid_input',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
messageIncludes: 'is not valid',
|
||||
}
|
||||
),
|
||||
expectRejects(getUsers<User[]>([['search.email', '%gmail%']]), {
|
||||
code: 'request.invalid_input',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
messageIncludes: 'is not valid',
|
||||
}),
|
||||
expectRejects(
|
||||
|
@ -231,7 +229,7 @@ describe('admin console user search params', () => {
|
|||
]),
|
||||
{
|
||||
code: 'request.invalid_input',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
messageIncludes: 'is not valid',
|
||||
}
|
||||
),
|
||||
|
@ -283,7 +281,7 @@ describe('admin console user search params - excludeRoleId', () => {
|
|||
['excludeRoleId', roles[0]!.id],
|
||||
]);
|
||||
|
||||
expect(headers['total-number']).toEqual('2');
|
||||
expect(headers.get('total-number')).toEqual('2');
|
||||
expect(json).toHaveLength(2);
|
||||
expect(json).toContainEqual(expect.objectContaining({ id: users[1]!.id }));
|
||||
expect(json).toContainEqual(expect.objectContaining({ id: users[2]!.id }));
|
||||
|
@ -295,7 +293,7 @@ describe('admin console user search params - excludeRoleId', () => {
|
|||
['excludeRoleId', roles[1]!.id],
|
||||
]);
|
||||
|
||||
expect(headers['total-number']).toEqual('1');
|
||||
expect(headers.get('total-number')).toEqual('1');
|
||||
expect(json).toHaveLength(1);
|
||||
expect(json).toContainEqual(expect.objectContaining({ id: users[2]!.id }));
|
||||
});
|
||||
|
@ -341,7 +339,7 @@ describe('admin console user search params - excludeOrganizationId', () => {
|
|||
['excludeOrganizationId', organizationApi.organizations[0]!.id],
|
||||
]);
|
||||
|
||||
expect(headers['total-number']).toEqual('1');
|
||||
expect(headers.get('total-number')).toEqual('1');
|
||||
expect(json).toHaveLength(1);
|
||||
expect(json).toContainEqual(expect.objectContaining({ id: userApi.users[2]!.id }));
|
||||
});
|
||||
|
@ -352,7 +350,7 @@ describe('admin console user search params - excludeOrganizationId', () => {
|
|||
['excludeOrganizationId', organizationApi.organizations[1]!.id],
|
||||
]);
|
||||
|
||||
expect(headers['total-number']).toEqual('1');
|
||||
expect(headers.get('total-number')).toEqual('1');
|
||||
expect(json).toHaveLength(1);
|
||||
expect(json).toContainEqual(expect.objectContaining({ id: userApi.users[0]!.id }));
|
||||
});
|
||||
|
@ -363,7 +361,7 @@ describe('admin console user search params - excludeOrganizationId', () => {
|
|||
['excludeOrganizationId', organizationApi.organizations[2]!.id],
|
||||
]);
|
||||
|
||||
expect(headers['total-number']).toEqual('2');
|
||||
expect(headers.get('total-number')).toEqual('2');
|
||||
expect(json).toHaveLength(2);
|
||||
expect(json).toContainEqual(expect.objectContaining({ id: userApi.users[0]!.id }));
|
||||
expect(json).toContainEqual(expect.objectContaining({ id: userApi.users[1]!.id }));
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
import crypto from 'node:crypto';
|
||||
|
||||
import { UsersPasswordEncryptionMethod, ConnectorType } from '@logto/schemas';
|
||||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import {
|
||||
mockSocialConnectorConfig,
|
||||
|
@ -25,9 +23,13 @@ import {
|
|||
import { clearConnectorsByTypes } from '#src/helpers/connector.js';
|
||||
import { createUserByAdmin, expectRejects } from '#src/helpers/index.js';
|
||||
import { createNewSocialUserWithUsernameAndPassword } from '#src/helpers/interactions.js';
|
||||
import { generateUsername, generateEmail, generatePhone, generatePassword } from '#src/utils.js';
|
||||
|
||||
const randomString = () => crypto.randomBytes(8).toString('hex');
|
||||
import {
|
||||
generateUsername,
|
||||
generateEmail,
|
||||
generatePhone,
|
||||
generatePassword,
|
||||
randomString,
|
||||
} from '#src/utils.js';
|
||||
|
||||
describe('admin console user management', () => {
|
||||
beforeAll(async () => {
|
||||
|
@ -62,8 +64,8 @@ describe('admin console user management', () => {
|
|||
profile: { gender: 'neutral' },
|
||||
});
|
||||
const { customData, profile } = await getUser(user.id);
|
||||
expect(customData).toStrictEqual({ foo: 'bar' });
|
||||
expect(profile).toStrictEqual({ gender: 'neutral' });
|
||||
expect({ ...customData }).toStrictEqual({ foo: 'bar' });
|
||||
expect({ ...profile }).toStrictEqual({ gender: 'neutral' });
|
||||
});
|
||||
|
||||
it('should fail when create user with conflict identifiers', async () => {
|
||||
|
@ -76,22 +78,22 @@ describe('admin console user management', () => {
|
|||
await createUserByAdmin({ username, password, primaryEmail, primaryPhone });
|
||||
await expectRejects(createUserByAdmin({ username, password }), {
|
||||
code: 'user.username_already_in_use',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
await expectRejects(createUserByAdmin({ primaryEmail }), {
|
||||
code: 'user.email_already_in_use',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
await expectRejects(createUserByAdmin({ primaryPhone }), {
|
||||
code: 'user.phone_already_in_use',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
});
|
||||
|
||||
it('should fail when get user by invalid id', async () => {
|
||||
await expectRejects(getUser('invalid-user-id'), {
|
||||
code: 'entity.not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -131,21 +133,21 @@ describe('admin console user management', () => {
|
|||
};
|
||||
|
||||
const updatedProfile = await updateUserProfile(user.id, profile);
|
||||
expect(updatedProfile).toStrictEqual(profile);
|
||||
expect(updatedProfile).toMatchObject(profile);
|
||||
|
||||
const patchProfile = {
|
||||
familyName: 'another name',
|
||||
website: 'https://logto.io/',
|
||||
};
|
||||
const updatedProfile2 = await updateUserProfile(user.id, patchProfile);
|
||||
expect(updatedProfile2).toStrictEqual({ ...profile, ...patchProfile });
|
||||
expect(updatedProfile2).toMatchObject({ ...profile, ...patchProfile });
|
||||
});
|
||||
|
||||
it('should respond 422 when no update data provided', async () => {
|
||||
const user = await createUserByAdmin();
|
||||
await expectRejects(updateUser(user.id, {}), {
|
||||
code: 'entity.invalid_input',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -160,17 +162,17 @@ describe('admin console user management', () => {
|
|||
|
||||
await expectRejects(updateUser(anotherUser.id, { username }), {
|
||||
code: 'user.username_already_in_use',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await expectRejects(updateUser(anotherUser.id, { primaryEmail }), {
|
||||
code: 'user.email_already_in_use',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await expectRejects(updateUser(anotherUser.id, { primaryPhone }), {
|
||||
code: 'user.phone_already_in_use',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -183,7 +185,7 @@ describe('admin console user management', () => {
|
|||
await deleteUser(user.id);
|
||||
|
||||
const response = await getUser(user.id).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode === 404).toBe(true);
|
||||
expect(response instanceof HTTPError && response.response.status === 404).toBe(true);
|
||||
});
|
||||
|
||||
it('should update user password successfully', async () => {
|
||||
|
@ -297,7 +299,7 @@ describe('admin console user management', () => {
|
|||
|
||||
it('should return 204 if password is correct', async () => {
|
||||
const user = await createUserByAdmin({ password: 'new_password' });
|
||||
expect(await verifyUserPassword(user.id, 'new_password')).toHaveProperty('statusCode', 204);
|
||||
expect(await verifyUserPassword(user.id, 'new_password')).toHaveProperty('status', 204);
|
||||
await deleteUser(user.id);
|
||||
});
|
||||
|
||||
|
@ -305,7 +307,7 @@ describe('admin console user management', () => {
|
|||
const user = await createUserByAdmin({ password: 'new_password' });
|
||||
await expectRejects(verifyUserPassword(user.id, 'wrong_password'), {
|
||||
code: 'session.invalid_credentials',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
await deleteUser(user.id);
|
||||
});
|
||||
|
@ -314,7 +316,7 @@ describe('admin console user management', () => {
|
|||
const user = await createUserByAdmin();
|
||||
await expectRejects(verifyUserPassword(user.id, ''), {
|
||||
code: 'guard.invalid_input',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -51,7 +51,7 @@ describe('application sign in experience', () => {
|
|||
setApplicationSignInExperience('non-existent-application', applicationSignInExperiences),
|
||||
{
|
||||
code: 'entity.not_exists_with_id',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -64,7 +64,7 @@ describe('application sign in experience', () => {
|
|||
),
|
||||
{
|
||||
code: 'application.third_party_application_only',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -112,7 +112,7 @@ describe('application sign in experience', () => {
|
|||
|
||||
await expectRejects(getApplicationSignInExperience(application.id), {
|
||||
code: 'entity.not_exists_with_id',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -76,7 +76,7 @@ describe('assign user consent organizations to application', () => {
|
|||
}),
|
||||
{
|
||||
code: 'entity.not_exists_with_id',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -88,7 +88,7 @@ describe('assign user consent organizations to application', () => {
|
|||
}),
|
||||
{
|
||||
code: 'entity.not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -104,7 +104,7 @@ describe('assign user consent organizations to application', () => {
|
|||
),
|
||||
{
|
||||
code: 'application.third_party_application_only',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -120,7 +120,7 @@ describe('assign user consent organizations to application', () => {
|
|||
),
|
||||
{
|
||||
code: 'organization.require_membership',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -78,7 +78,7 @@ describe('assign user consent scopes to application', () => {
|
|||
}),
|
||||
{
|
||||
code: 'application.third_party_application_only',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -90,7 +90,7 @@ describe('assign user consent scopes to application', () => {
|
|||
}),
|
||||
{
|
||||
code: 'application.user_consent_scopes_not_found',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -102,7 +102,7 @@ describe('assign user consent scopes to application', () => {
|
|||
}),
|
||||
{
|
||||
code: 'application.user_consent_scopes_not_found',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -130,7 +130,7 @@ describe('assign user consent scopes to application', () => {
|
|||
it('should return 404 when trying to get consent scopes from non-existing application', async () => {
|
||||
await expectRejects(getUserConsentScopes('non-existing-application'), {
|
||||
code: 'entity.not_exists_with_id',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -184,7 +184,7 @@ describe('assign user consent scopes to application', () => {
|
|||
),
|
||||
{
|
||||
code: 'entity.not_exists_with_id',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -198,7 +198,7 @@ describe('assign user consent scopes to application', () => {
|
|||
),
|
||||
{
|
||||
code: 'entity.not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -210,7 +210,7 @@ describe('assign user consent scopes to application', () => {
|
|||
),
|
||||
{
|
||||
code: 'entity.not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -222,7 +222,7 @@ describe('assign user consent scopes to application', () => {
|
|||
),
|
||||
{
|
||||
code: 'entity.not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { ApplicationType, RoleType } from '@logto/schemas';
|
||||
import { generateStandardId } from '@logto/shared';
|
||||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import {
|
||||
createApplication,
|
||||
|
@ -27,7 +27,7 @@ describe('admin console application management (roles)', () => {
|
|||
const application = await createApplication(generateStandardId(), applicationType);
|
||||
|
||||
const response = await getApplicationRoles(application.id).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode === 422).toBe(true);
|
||||
expect(response instanceof HTTPError && response.response.status === 422).toBe(true);
|
||||
});
|
||||
|
||||
it('should assign roles to app and get list successfully', async () => {
|
||||
|
@ -59,7 +59,7 @@ describe('admin console application management (roles)', () => {
|
|||
await assignRolesToApplication(application.id, [role.id]);
|
||||
await expectRejects(assignRolesToApplication(application.id, [role.id]), {
|
||||
code: 'application.role_exists',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -70,7 +70,7 @@ describe('admin console application management (roles)', () => {
|
|||
|
||||
await expectRejects(assignRolesToApplication(application.id, [role.id]), {
|
||||
code: 'application.invalid_type',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -114,7 +114,7 @@ describe('admin console application management (roles)', () => {
|
|||
const response = await deleteRoleFromApplication(application.id, role.id).catch(
|
||||
(error: unknown) => error
|
||||
);
|
||||
expect(response instanceof HTTPError && response.response.statusCode === 404).toBe(true);
|
||||
expect(response instanceof HTTPError && response.response.status === 404).toBe(true);
|
||||
});
|
||||
|
||||
// This case tests GET operation on applications and filter by `types` parameter and `search` parameter.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ApplicationType } from '@logto/schemas';
|
||||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import {
|
||||
createApplication,
|
||||
|
@ -31,7 +31,7 @@ describe('admin console application', () => {
|
|||
createApplication('test-create-app', ApplicationType.Native, {
|
||||
isThirdParty: true,
|
||||
}),
|
||||
{ code: 'application.invalid_third_party_application_type', statusCode: 400 }
|
||||
{ code: 'application.invalid_third_party_application_type', status: 400 }
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -86,7 +86,7 @@ describe('admin console application', () => {
|
|||
}),
|
||||
{
|
||||
code: 'application.protected_application_subdomain_exists',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
await deleteApplication(application.id);
|
||||
|
@ -95,7 +95,7 @@ describe('admin console application', () => {
|
|||
it('should throw error when creating a protected application with invalid type', async () => {
|
||||
await expectRejects(createApplication('test-create-app', ApplicationType.Protected), {
|
||||
code: 'application.protected_app_metadata_is_required',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -120,7 +120,7 @@ describe('admin console application', () => {
|
|||
|
||||
expect(updatedApplication.description).toBe(newApplicationDescription);
|
||||
expect(updatedApplication.oidcClientMetadata.redirectUris).toEqual(newRedirectUris);
|
||||
expect(updatedApplication.customClientMetadata).toStrictEqual({
|
||||
expect({ ...updatedApplication.customClientMetadata }).toStrictEqual({
|
||||
rotateRefreshToken: true,
|
||||
refreshTokenTtlInDays: 10,
|
||||
});
|
||||
|
@ -256,6 +256,6 @@ describe('admin console application', () => {
|
|||
await deleteApplication(application.id);
|
||||
|
||||
const response = await getApplication(application.id).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode === 404).toBe(true);
|
||||
expect(response instanceof HTTPError && response.response.status === 404).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,19 +1,20 @@
|
|||
import { got } from 'got';
|
||||
import ky from 'ky';
|
||||
|
||||
import { logtoConsoleUrl } from '#src/constants.js';
|
||||
|
||||
describe('social connector form post callback', () => {
|
||||
const request = got.extend({
|
||||
const request = ky.extend({
|
||||
prefixUrl: new URL(logtoConsoleUrl),
|
||||
});
|
||||
|
||||
it('should redirect to the same path with query string', async () => {
|
||||
const response = await request.post('callback/some_connector_id', {
|
||||
json: { some: 'data' },
|
||||
followRedirect: false,
|
||||
redirect: 'manual',
|
||||
throwHttpErrors: false,
|
||||
});
|
||||
|
||||
expect(response.statusCode).toBe(303);
|
||||
expect(response.headers.location).toBe('/callback/some_connector_id?some=data');
|
||||
expect(response.status).toBe(303);
|
||||
expect(response.headers.get('location')).toBe('/callback/some_connector_id?some=data');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import {
|
||||
mockEmailConnectorConfig,
|
||||
|
@ -160,7 +160,7 @@ test('create connector with non-exist connectorId', async () => {
|
|||
await cleanUpConnectorTable();
|
||||
await expectRejects(postConnector({ connectorId: 'non-exist-id' }), {
|
||||
code: 'connector.not_found_with_connector_id',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -170,7 +170,7 @@ test('create non standard social connector with target', async () => {
|
|||
postConnector({ connectorId: mockSocialConnectorId, metadata: { target: 'target' } }),
|
||||
{
|
||||
code: 'connector.cannot_overwrite_metadata_for_non_standard_connector',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -180,7 +180,7 @@ test('create duplicated social connector', async () => {
|
|||
await postConnector({ connectorId: mockSocialConnectorId });
|
||||
await expectRejects(postConnector({ connectorId: mockSocialConnectorId }), {
|
||||
code: 'connector.multiple_instances_not_supported',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -189,7 +189,7 @@ test('override metadata for non-standard social connector', async () => {
|
|||
const { id } = await postConnector({ connectorId: mockSocialConnectorId });
|
||||
await expectRejects(updateConnectorConfig(id, {}, { target: 'target' }), {
|
||||
code: 'connector.cannot_overwrite_metadata_for_non_standard_connector',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import {
|
||||
listCustomPhrases,
|
||||
|
|
|
@ -19,15 +19,15 @@ describe('admin console dashboard', () => {
|
|||
it('non authorized request should return 401', async () => {
|
||||
await expectRejects(api.get('dashboard/users/total'), {
|
||||
code: 'auth.authorization_header_missing',
|
||||
statusCode: 401,
|
||||
status: 401,
|
||||
});
|
||||
await expectRejects(api.get('dashboard/users/new'), {
|
||||
code: 'auth.authorization_header_missing',
|
||||
statusCode: 401,
|
||||
status: 401,
|
||||
});
|
||||
await expectRejects(api.get('dashboard/users/active'), {
|
||||
code: 'auth.authorization_header_missing',
|
||||
statusCode: 401,
|
||||
status: 401,
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import { createDomain, deleteDomain, getDomain, getDomains } from '#src/api/domain.js';
|
||||
import { generateDomain } from '#src/utils.js';
|
||||
|
@ -27,7 +27,7 @@ describe('domains', () => {
|
|||
await createDomain();
|
||||
|
||||
const response = await createDomain().catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(422);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(422);
|
||||
});
|
||||
|
||||
it('should get domain detail successfully', async () => {
|
||||
|
@ -40,7 +40,7 @@ describe('domains', () => {
|
|||
it('should return 404 if domain does not exist', async () => {
|
||||
const response = await getDomain('non_existent_domain').catch((error: unknown) => error);
|
||||
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should delete domain successfully', async () => {
|
||||
|
@ -49,6 +49,6 @@ describe('domains', () => {
|
|||
await deleteDomain(domain.id);
|
||||
|
||||
const response = await getDomain(domain.id).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,6 +2,6 @@ import { api } from '#src/api/index.js';
|
|||
|
||||
describe('health check', () => {
|
||||
it('should have a health state', async () => {
|
||||
expect(await api.get('status')).toHaveProperty('statusCode', 204);
|
||||
expect(await api.get('status')).toHaveProperty('status', 204);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -19,10 +19,10 @@ describe('hooks', () => {
|
|||
.patch(`hooks/${created.id}`, { json: { events: [HookEvent.PostSignIn] } })
|
||||
.json<Hook>()
|
||||
).toMatchObject({ ...created, events: [HookEvent.PostSignIn] });
|
||||
expect(await authedAdminApi.delete(`hooks/${created.id}`)).toHaveProperty('statusCode', 204);
|
||||
expect(await authedAdminApi.delete(`hooks/${created.id}`)).toHaveProperty('status', 204);
|
||||
await expectRejects(authedAdminApi.get(`hooks/${created.id}`), {
|
||||
code: 'entity.not_exists_with_id',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -48,10 +48,10 @@ describe('hooks', () => {
|
|||
...created,
|
||||
event: HookEvent.PostSignIn,
|
||||
});
|
||||
expect(await authedAdminApi.delete(`hooks/${created.id}`)).toHaveProperty('statusCode', 204);
|
||||
expect(await authedAdminApi.delete(`hooks/${created.id}`)).toHaveProperty('status', 204);
|
||||
await expectRejects(authedAdminApi.get(`hooks/${created.id}`), {
|
||||
code: 'entity.not_exists_with_id',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -61,8 +61,8 @@ describe('hooks', () => {
|
|||
|
||||
const response = await authedAdminApi.get('hooks?page=1&page_size=20');
|
||||
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response.headers).toHaveProperty('total-number');
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get('total-number')).toEqual(expect.any(String));
|
||||
|
||||
// Clean up
|
||||
await authedAdminApi.delete(`hooks/${created.id}`);
|
||||
|
@ -78,7 +78,7 @@ describe('hooks', () => {
|
|||
};
|
||||
await expectRejects(authedAdminApi.post('hooks', { json: payload }), {
|
||||
code: 'guard.invalid_input',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -91,7 +91,7 @@ describe('hooks', () => {
|
|||
};
|
||||
await expectRejects(authedAdminApi.post('hooks', { json: payload }), {
|
||||
code: 'hook.missing_events',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -102,14 +102,14 @@ describe('hooks', () => {
|
|||
|
||||
await expectRejects(authedAdminApi.patch('hooks/invalid_id', { json: payload }), {
|
||||
code: 'entity.not_exists',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw error if regenerate a hook signing key with a invalid hook id', async () => {
|
||||
await expectRejects(authedAdminApi.patch('hooks/invalid_id/signing-key'), {
|
||||
code: 'entity.not_exists',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -37,7 +37,7 @@ describe('hook testing', () => {
|
|||
const response = await authedAdminApi.post(`hooks/${created.id}/test`, {
|
||||
json: { events: [HookEvent.PostSignIn], config: { url: responseSuccessEndpoint } },
|
||||
});
|
||||
expect(response.statusCode).toBe(204);
|
||||
expect(response.status).toBe(204);
|
||||
|
||||
// Clean Up
|
||||
await authedAdminApi.delete(`hooks/${created.id}`);
|
||||
|
@ -51,7 +51,7 @@ describe('hook testing', () => {
|
|||
}),
|
||||
{
|
||||
code: 'entity.not_exists_with_id',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -65,7 +65,7 @@ describe('hook testing', () => {
|
|||
}),
|
||||
{
|
||||
code: 'hook.send_test_payload_failed',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -82,7 +82,7 @@ describe('hook testing', () => {
|
|||
}),
|
||||
{
|
||||
code: 'hook.endpoint_responded_with_error',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ describe('Interaction details guard checking', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -38,7 +38,7 @@ describe('Interaction details guard checking', () => {
|
|||
it('DELETE /interaction', async () => {
|
||||
await expectRejects(client.send(deleteInteraction), {
|
||||
code: 'session.not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -49,7 +49,7 @@ describe('Interaction details guard checking', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -62,7 +62,7 @@ describe('Interaction details guard checking', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -75,7 +75,7 @@ describe('Interaction details guard checking', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -88,7 +88,7 @@ describe('Interaction details guard checking', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -96,14 +96,14 @@ describe('Interaction details guard checking', () => {
|
|||
it('DELETE /interaction/profile', async () => {
|
||||
await expectRejects(client.send(deleteInteractionProfile), {
|
||||
code: 'session.not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
});
|
||||
});
|
||||
|
||||
it('POST /interaction/submit', async () => {
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'session.not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -116,7 +116,7 @@ describe('Interaction details guard checking', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -128,7 +128,7 @@ describe('Interaction details guard checking', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -26,7 +26,7 @@ describe('Interaction details results checking', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.verification_session_not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -41,7 +41,7 @@ describe('Interaction details results checking', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.verification_session_not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -55,7 +55,7 @@ describe('Interaction details results checking', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.verification_session_not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -69,7 +69,7 @@ describe('Interaction details results checking', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.verification_session_not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -78,7 +78,7 @@ describe('Interaction details results checking', () => {
|
|||
const client = await initClient();
|
||||
await expectRejects(client.send(deleteInteractionProfile), {
|
||||
code: 'session.verification_session_not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -86,7 +86,7 @@ describe('Interaction details results checking', () => {
|
|||
const client = await initClient();
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'session.verification_session_not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -100,7 +100,7 @@ describe('Interaction details results checking', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.verification_session_not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -113,7 +113,7 @@ describe('Interaction details results checking', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.verification_session_not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -31,7 +31,7 @@ describe('PATCH /interaction/identifiers', () => {
|
|||
}),
|
||||
{
|
||||
code: 'user.suspended',
|
||||
statusCode: 401,
|
||||
status: 401,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -1,10 +1,15 @@
|
|||
import { InteractionEvent } from '@logto/schemas';
|
||||
import { ConnectorType, InteractionEvent } from '@logto/schemas';
|
||||
|
||||
import { putInteraction, sendVerificationCode } from '#src/api/interaction.js';
|
||||
import { initClient } from '#src/helpers/client.js';
|
||||
import { clearConnectorsByTypes } from '#src/helpers/connector.js';
|
||||
import { expectRejects } from '#src/helpers/index.js';
|
||||
import { generateEmail, generatePhone } from '#src/utils.js';
|
||||
|
||||
beforeAll(async () => {
|
||||
await clearConnectorsByTypes([ConnectorType.Email, ConnectorType.Sms]);
|
||||
});
|
||||
|
||||
/**
|
||||
* Note: These test cases are designed to cover exceptional scenarios of API calls that
|
||||
* cannot be covered within the auth flow.
|
||||
|
@ -23,7 +28,7 @@ describe('POST /interaction/verification/verification-code', () => {
|
|||
}),
|
||||
{
|
||||
code: 'connector.not_found',
|
||||
statusCode: 501,
|
||||
status: 501,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -41,7 +46,7 @@ describe('POST /interaction/verification/verification-code', () => {
|
|||
}),
|
||||
{
|
||||
code: 'connector.not_found',
|
||||
statusCode: 501,
|
||||
status: 501,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -27,7 +27,7 @@ describe('PUT /interaction/event', () => {
|
|||
}),
|
||||
{
|
||||
code: 'auth.forbidden',
|
||||
statusCode: 403,
|
||||
status: 403,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -41,7 +41,7 @@ describe('PUT /interaction/event', () => {
|
|||
}),
|
||||
{
|
||||
code: 'auth.forbidden',
|
||||
statusCode: 403,
|
||||
status: 403,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -65,7 +65,7 @@ describe('PUT /interaction/event', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.interaction_not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -75,7 +75,7 @@ describe('PUT /interaction/event', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.interaction_not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -96,7 +96,7 @@ describe('PUT /interaction/event', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.interaction_not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -111,7 +111,7 @@ describe('PUT /interaction/event', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.interaction_not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -54,7 +54,7 @@ describe('PUT /interaction', () => {
|
|||
}),
|
||||
{
|
||||
code: 'user.sign_in_method_not_enabled',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -69,7 +69,7 @@ describe('PUT /interaction', () => {
|
|||
}),
|
||||
{
|
||||
code: 'user.sign_in_method_not_enabled',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -100,7 +100,7 @@ describe('PUT /interaction', () => {
|
|||
}),
|
||||
{
|
||||
code: 'user.sign_in_method_not_enabled',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -114,7 +114,7 @@ describe('PUT /interaction', () => {
|
|||
}),
|
||||
{
|
||||
code: 'user.sign_in_method_not_enabled',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -140,7 +140,7 @@ describe('PUT /interaction', () => {
|
|||
}),
|
||||
{
|
||||
code: 'verification_code.not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -155,7 +155,7 @@ describe('PUT /interaction', () => {
|
|||
}),
|
||||
{
|
||||
code: 'verification_code.not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -177,7 +177,7 @@ describe('PUT /interaction', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.invalid_connector_id',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -194,7 +194,7 @@ describe('PUT /interaction', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.connector_session_not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -64,14 +64,14 @@ describe('reset password', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.new_password_required_in_profile',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(putInteractionProfile, { password: userProfile.password });
|
||||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.same_password',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
const newPasswordRecord = generatePassword();
|
||||
|
@ -124,14 +124,14 @@ describe('reset password', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.new_password_required_in_profile',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(putInteractionProfile, { password: userProfile.password });
|
||||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.same_password',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
const newPasswordRecord = generatePassword();
|
||||
|
|
|
@ -40,7 +40,7 @@ describe('reset password flow sad path', () => {
|
|||
await client.successSend(putInteractionProfile, { password: generatePassword() });
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.user_not_exist',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
|
||||
// Clear
|
||||
|
@ -68,7 +68,7 @@ describe('reset password flow sad path', () => {
|
|||
await client.successSend(putInteractionProfile, { password: generatePassword() });
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.user_not_exist',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
|
||||
// Clear
|
||||
|
@ -104,7 +104,7 @@ describe('reset password flow sad path', () => {
|
|||
await client.successSend(putInteractionProfile, { password: generatePassword() });
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.suspended',
|
||||
statusCode: 401,
|
||||
status: 401,
|
||||
});
|
||||
|
||||
// Clear
|
||||
|
|
|
@ -39,7 +39,7 @@ const registerWithMfa = async () => {
|
|||
|
||||
const { codes } = await expectRejects<{ codes: string[] }>(client.submitInteraction(), {
|
||||
code: 'session.mfa.backup_code_required',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
});
|
||||
|
||||
await client.send(postInteractionBindMfa, {
|
||||
|
@ -81,7 +81,7 @@ describe('sign in and verify mfa (Backup Code)', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'session.mfa.require_mfa_verification',
|
||||
statusCode: 403,
|
||||
status: 403,
|
||||
});
|
||||
|
||||
await deleteUser(id);
|
||||
|
|
|
@ -70,7 +70,7 @@ describe('register with mfa (mandatory TOTP)', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.missing_mfa',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -94,7 +94,7 @@ describe('register with mfa (mandatory TOTP)', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.mfa.invalid_totp_code',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -151,7 +151,7 @@ describe('sign in and fulfill mfa (mandatory TOTP)', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.missing_mfa',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await deleteUser(user.id);
|
||||
|
@ -187,7 +187,7 @@ describe('sign in and fulfill mfa (user-controlled TOTP)', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.missing_mfa',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await deleteUser(user.id);
|
||||
|
@ -207,7 +207,7 @@ describe('sign in and fulfill mfa (user-controlled TOTP)', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.missing_mfa',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(skipMfaBinding);
|
||||
|
@ -241,7 +241,7 @@ describe('sign in and verify mfa (TOTP)', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'session.mfa.require_mfa_verification',
|
||||
statusCode: 403,
|
||||
status: 403,
|
||||
});
|
||||
|
||||
await deleteUser(id);
|
||||
|
|
|
@ -146,7 +146,7 @@ describe('register with passwordless identifier', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.missing_profile',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(patchInteractionProfile, {
|
||||
|
@ -251,7 +251,7 @@ describe('register with passwordless identifier', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.missing_profile',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(patchInteractionProfile, {
|
||||
|
@ -322,7 +322,7 @@ describe('register with passwordless identifier', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.email_already_in_use',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(deleteInteractionProfile);
|
||||
|
@ -377,7 +377,7 @@ describe('register with passwordless identifier', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.phone_already_in_use',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(deleteInteractionProfile);
|
||||
|
|
|
@ -40,7 +40,7 @@ describe('Register with identifiers sad path', () => {
|
|||
}),
|
||||
{
|
||||
code: 'auth.forbidden',
|
||||
statusCode: 403,
|
||||
status: 403,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -67,7 +67,7 @@ describe('Register with identifiers sad path', () => {
|
|||
}),
|
||||
{
|
||||
code: 'user.sign_in_method_not_enabled',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -94,7 +94,7 @@ describe('Register with identifiers sad path', () => {
|
|||
}),
|
||||
{
|
||||
code: 'user.sign_in_method_not_enabled',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -125,7 +125,7 @@ describe('Register with identifiers sad path', () => {
|
|||
}),
|
||||
{
|
||||
code: 'user.sign_in_method_not_enabled',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -133,7 +133,7 @@ describe('test register with email with SSO feature', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.sso_enabled',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -127,7 +127,7 @@ describe('Sign-in flow using verification-code identifiers', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.user_not_exist',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
|
||||
await client.successSend(putInteractionEvent, { event: InteractionEvent.Register });
|
||||
|
@ -169,7 +169,7 @@ describe('Sign-in flow using verification-code identifiers', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.user_not_exist',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
|
||||
await client.successSend(putInteractionEvent, { event: InteractionEvent.Register });
|
||||
|
@ -214,7 +214,7 @@ describe('Sign-in flow using verification-code identifiers', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.missing_profile',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
// Fulfill user profile
|
||||
|
@ -276,7 +276,7 @@ describe('Sign-in flow using verification-code identifiers', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.missing_profile',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
// Fulfill user profile with existing password
|
||||
|
@ -287,7 +287,7 @@ describe('Sign-in flow using verification-code identifiers', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.password_exists_in_profile',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
});
|
||||
|
||||
await client.successSend(putInteractionProfile, {
|
||||
|
@ -333,7 +333,7 @@ describe('Sign-in flow using verification-code identifiers', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.missing_profile',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
// Fulfill user profile with existing password
|
||||
|
@ -344,7 +344,7 @@ describe('Sign-in flow using verification-code identifiers', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.username_already_in_use',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(putInteractionProfile, {
|
||||
|
|
|
@ -43,7 +43,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
|
|||
}),
|
||||
{
|
||||
code: 'auth.forbidden',
|
||||
statusCode: 403,
|
||||
status: 403,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -81,7 +81,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
|
|||
}),
|
||||
{
|
||||
code: 'user.sign_in_method_not_enabled',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -99,7 +99,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
|
|||
}),
|
||||
{
|
||||
code: 'user.sign_in_method_not_enabled',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -130,7 +130,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
|
|||
}),
|
||||
{
|
||||
code: 'verification_code.code_mismatch',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -141,7 +141,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
|
|||
}),
|
||||
{
|
||||
code: 'verification_code.email_mismatch',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -169,7 +169,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
|
|||
}),
|
||||
{
|
||||
code: 'verification_code.code_mismatch',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -180,7 +180,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
|
|||
}),
|
||||
{
|
||||
code: 'verification_code.phone_mismatch',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -207,7 +207,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.user_not_exist',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -233,7 +233,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.user_not_exist',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -263,7 +263,7 @@ describe('Sign-in flow sad path using verification-code identifiers', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.suspended',
|
||||
statusCode: 401,
|
||||
status: 401,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -100,7 +100,7 @@ describe('test sign-in with email passcode identifier with SSO feature', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.sso_enabled',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -161,7 +161,7 @@ describe('Sign-in flow using password identifiers', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.missing_profile',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(sendVerificationCode, {
|
||||
|
@ -223,7 +223,7 @@ describe('Sign-in flow using password identifiers', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.missing_profile',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(sendVerificationCode, {
|
||||
|
|
|
@ -35,7 +35,7 @@ describe('Sign-in flow sad path using password identifiers', () => {
|
|||
}),
|
||||
{
|
||||
code: 'auth.forbidden',
|
||||
statusCode: 403,
|
||||
status: 403,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -51,7 +51,7 @@ describe('Sign-in flow sad path using password identifiers', () => {
|
|||
}),
|
||||
{
|
||||
code: 'auth.forbidden',
|
||||
statusCode: 403,
|
||||
status: 403,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -67,7 +67,7 @@ describe('Sign-in flow sad path using password identifiers', () => {
|
|||
}),
|
||||
{
|
||||
code: 'auth.forbidden',
|
||||
statusCode: 403,
|
||||
status: 403,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -91,7 +91,7 @@ describe('Sign-in flow sad path using password identifiers', () => {
|
|||
}),
|
||||
{
|
||||
code: 'user.sign_in_method_not_enabled',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -107,7 +107,7 @@ describe('Sign-in flow sad path using password identifiers', () => {
|
|||
}),
|
||||
{
|
||||
code: 'user.sign_in_method_not_enabled',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -123,7 +123,7 @@ describe('Sign-in flow sad path using password identifiers', () => {
|
|||
}),
|
||||
{
|
||||
code: 'user.sign_in_method_not_enabled',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -141,7 +141,7 @@ describe('Sign-in flow sad path using password identifiers', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.invalid_credentials',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -159,7 +159,7 @@ describe('Sign-in flow sad path using password identifiers', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.invalid_credentials',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -177,7 +177,7 @@ describe('Sign-in flow sad path using password identifiers', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.invalid_credentials',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -199,7 +199,7 @@ describe('Sign-in flow sad path using password identifiers', () => {
|
|||
}),
|
||||
{
|
||||
code: 'user.suspended',
|
||||
statusCode: 401,
|
||||
status: 401,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -99,7 +99,7 @@ describe('test sign-in with email passcode identifier with SSO feature', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.sso_enabled',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
await deleteUser(user.id);
|
||||
|
|
|
@ -6,18 +6,20 @@ import { updateSignInExperience } from '#src/api/sign-in-experience.js';
|
|||
import { createSsoConnector, deleteSsoConnectorById } from '#src/api/sso-connector.js';
|
||||
import { logtoUrl } from '#src/constants.js';
|
||||
import { initClient } from '#src/helpers/client.js';
|
||||
import { randomString } from '#src/utils.js';
|
||||
|
||||
describe('Single Sign On Happy Path', () => {
|
||||
const connectorIdMap = new Map<string, SsoConnectorMetadata>();
|
||||
|
||||
const state = 'foo_state';
|
||||
const redirectUri = 'http://foo.dev/callback';
|
||||
const domain = `foo${randomString()}.com`;
|
||||
|
||||
beforeAll(async () => {
|
||||
const { id, connectorName } = await createSsoConnector({
|
||||
providerName: SsoProviderName.OIDC,
|
||||
connectorName: 'test-oidc',
|
||||
domains: ['foo.com'],
|
||||
connectorName: `test-oidc-${randomString()}`,
|
||||
domains: [domain],
|
||||
config: {
|
||||
clientId: 'foo',
|
||||
clientSecret: 'bar',
|
||||
|
@ -60,7 +62,7 @@ describe('Single Sign On Happy Path', () => {
|
|||
const client = await initClient();
|
||||
|
||||
const response = await client.send(getSsoConnectorsByEmail, {
|
||||
email: 'bar@foo.com',
|
||||
email: 'bar@' + domain,
|
||||
});
|
||||
|
||||
expect(response.length).toBeGreaterThan(0);
|
||||
|
@ -88,7 +90,7 @@ describe('Single Sign On Happy Path', () => {
|
|||
});
|
||||
|
||||
const response = await client.send(getSsoConnectorsByEmail, {
|
||||
email: 'bar@foo.com',
|
||||
email: 'bar@' + domain,
|
||||
});
|
||||
|
||||
expect(response.length).toBe(0);
|
||||
|
|
|
@ -9,6 +9,7 @@ import { putInteraction } from '#src/api/interaction.js';
|
|||
import { createSsoConnector, deleteSsoConnectorById } from '#src/api/sso-connector.js';
|
||||
import { initClient } from '#src/helpers/client.js';
|
||||
import { expectRejects } from '#src/helpers/index.js';
|
||||
import { randomString } from '#src/utils.js';
|
||||
|
||||
describe('Single Sign On Sad Path', () => {
|
||||
const state = 'foo_state';
|
||||
|
@ -34,7 +35,7 @@ describe('Single Sign On Sad Path', () => {
|
|||
it('should throw if connector config is invalid', async () => {
|
||||
const { id } = await createSsoConnector({
|
||||
providerName: SsoProviderName.OIDC,
|
||||
connectorName: 'test-oidc',
|
||||
connectorName: `test-oidc-${randomString()}`,
|
||||
});
|
||||
|
||||
const client = await initClient();
|
||||
|
@ -82,7 +83,7 @@ describe('Single Sign On Sad Path', () => {
|
|||
postSamlAssertion({ connectorId, RelayState: 'foo', SAMLResponse: samlAssertion }),
|
||||
{
|
||||
code: 'session.not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -103,7 +104,7 @@ describe('Single Sign On Sad Path', () => {
|
|||
postSamlAssertion({ connectorId, RelayState, SAMLResponse: samlAssertion }),
|
||||
{
|
||||
code: 'connector.authorization_failed',
|
||||
statusCode: 401,
|
||||
status: 401,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
@ -47,7 +47,7 @@ describe('social sign-in', () => {
|
|||
await clearConnectorsByTypes([ConnectorType.Social, ConnectorType.Email, ConnectorType.Sms]);
|
||||
});
|
||||
|
||||
describe.only('register and sign-in', () => {
|
||||
describe('register and sign-in', () => {
|
||||
const socialUserId = generateUserId();
|
||||
|
||||
it('register with social', async () => {
|
||||
|
@ -68,7 +68,7 @@ describe('social sign-in', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.identity_not_exist',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(putInteractionEvent, { event: InteractionEvent.Register });
|
||||
|
@ -124,7 +124,7 @@ describe('social sign-in', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.identity_not_exist',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(putInteractionEvent, { event: InteractionEvent.Register });
|
||||
|
@ -137,7 +137,7 @@ describe('social sign-in', () => {
|
|||
const { primaryEmail, identities } = await getUser(uid);
|
||||
expect(primaryEmail).toBe(socialEmail);
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
expect(identities[mockSocialConnectorTarget]).toStrictEqual({
|
||||
expect(identities[mockSocialConnectorTarget]).toMatchObject({
|
||||
details: {
|
||||
email: expect.any(String),
|
||||
id: expect.any(String),
|
||||
|
@ -176,7 +176,7 @@ describe('social sign-in', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.identity_not_exist',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(putInteractionEvent, { event: InteractionEvent.Register });
|
||||
|
@ -213,7 +213,7 @@ describe('social sign-in', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.identity_not_exist',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(putInteractionEvent, { event: InteractionEvent.Register });
|
||||
|
@ -255,7 +255,7 @@ describe('social sign-in', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.identity_not_exist',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(patchInteractionIdentifiers, {
|
||||
|
@ -319,7 +319,7 @@ describe('social sign-in', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.identity_not_exist',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(putInteractionEvent, { event: InteractionEvent.Register });
|
||||
|
@ -327,7 +327,7 @@ describe('social sign-in', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.missing_profile',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(patchInteractionProfile, { username: generateUsername() });
|
||||
|
@ -363,7 +363,7 @@ describe('social sign-in', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.identity_not_exist',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(putInteractionEvent, { event: InteractionEvent.Register });
|
||||
|
@ -400,7 +400,7 @@ describe('social sign-in', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.identity_not_exist',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(putInteractionEvent, { event: InteractionEvent.Register });
|
||||
|
|
|
@ -73,7 +73,7 @@ describe('Social identifier interaction sad path', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.identity_not_exist',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(putInteractionEvent, { event: InteractionEvent.Register });
|
||||
|
@ -101,7 +101,7 @@ describe('Social identifier interaction sad path', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.insufficient_info',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -121,7 +121,7 @@ describe('Social identifier interaction sad path', () => {
|
|||
}),
|
||||
{
|
||||
code: 'guard.invalid_input',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -141,7 +141,7 @@ describe('Social identifier interaction sad path', () => {
|
|||
}),
|
||||
{
|
||||
code: 'connector.unexpected_type',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -154,7 +154,7 @@ describe('Social identifier interaction sad path', () => {
|
|||
}),
|
||||
{
|
||||
code: 'connector.unexpected_type',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -173,7 +173,7 @@ describe('Social identifier interaction sad path', () => {
|
|||
}),
|
||||
{
|
||||
code: 'entity.not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -195,7 +195,7 @@ describe('Social identifier interaction sad path', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.invalid_connector_id',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -217,7 +217,7 @@ describe('Social identifier interaction sad path', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.invalid_connector_id',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -240,7 +240,7 @@ describe('Social identifier interaction sad path', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.connector_session_not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -252,7 +252,7 @@ describe('Social identifier interaction sad path', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.connector_session_not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -281,7 +281,7 @@ describe('Social identifier interaction sad path', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.connector_session_not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
|
||||
|
@ -293,7 +293,7 @@ describe('Social identifier interaction sad path', () => {
|
|||
}),
|
||||
{
|
||||
code: 'session.connector_session_not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
});
|
||||
|
@ -318,7 +318,7 @@ describe('Social identifier interaction sad path', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.suspended',
|
||||
statusCode: 401,
|
||||
status: 401,
|
||||
});
|
||||
|
||||
// Reset
|
||||
|
@ -350,7 +350,7 @@ describe('Social identifier interaction sad path', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'user.identity_not_exist',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
await client.successSend(putInteractionEvent, { event: InteractionEvent.Register });
|
||||
|
@ -361,7 +361,7 @@ describe('Social identifier interaction sad path', () => {
|
|||
|
||||
await expectRejects(client.submitInteraction(), {
|
||||
code: 'session.connector_session_not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -22,7 +22,7 @@ describe('logs', () => {
|
|||
it('should throw on getting non-exist log detail', async () => {
|
||||
await expectRejects(getLog('non-exist-log-id'), {
|
||||
code: 'entity.not_exists_with_id',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -61,14 +61,14 @@ describe('admin console sign-in experience', () => {
|
|||
expect(privateKeys).toHaveLength(1);
|
||||
await expectRejects(deleteOidcKey(LogtoOidcConfigKeyType.PrivateKeys, privateKeys[0]!.id), {
|
||||
code: 'oidc.key_required',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
|
||||
const cookieKeys = await getOidcKeys(LogtoOidcConfigKeyType.CookieKeys);
|
||||
expect(cookieKeys).toHaveLength(1);
|
||||
await expectRejects(deleteOidcKey(LogtoOidcConfigKeyType.CookieKeys, cookieKeys[0]!.id), {
|
||||
code: 'oidc.key_required',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -141,11 +141,11 @@ describe('admin console sign-in experience', () => {
|
|||
|
||||
await expectRejects(getJwtCustomizer('access-token'), {
|
||||
code: 'entity.not_exists',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
await expectRejects(deleteJwtCustomizer('access-token'), {
|
||||
code: 'entity.not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
const accessToken = await upsertJwtCustomizer('access-token', accessTokenJwtCustomizerPayload);
|
||||
expect(accessToken).toMatchObject(accessTokenJwtCustomizerPayload);
|
||||
|
@ -164,7 +164,7 @@ describe('admin console sign-in experience', () => {
|
|||
await expect(deleteJwtCustomizer('access-token')).resolves.not.toThrow();
|
||||
await expectRejects(getJwtCustomizer('access-token'), {
|
||||
code: 'entity.not_exists',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -177,11 +177,11 @@ describe('admin console sign-in experience', () => {
|
|||
|
||||
await expectRejects(getJwtCustomizer('client-credentials'), {
|
||||
code: 'entity.not_exists',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
await expectRejects(deleteJwtCustomizer('client-credentials'), {
|
||||
code: 'entity.not_found',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
const clientCredentials = await upsertJwtCustomizer(
|
||||
'client-credentials',
|
||||
|
@ -203,7 +203,7 @@ describe('admin console sign-in experience', () => {
|
|||
await expect(deleteJwtCustomizer('client-credentials')).resolves.not.toThrow();
|
||||
await expectRejects(getJwtCustomizer('client-credentials'), {
|
||||
code: 'entity.not_exists',
|
||||
statusCode: 404,
|
||||
status: 404,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { got } from 'got';
|
||||
import ky from 'ky';
|
||||
|
||||
import { logtoConsoleUrl, logtoUrl } from '#src/constants.js';
|
||||
import {
|
||||
|
@ -11,35 +11,35 @@ import { expectRejects } from '#src/helpers/index.js';
|
|||
|
||||
describe('me', () => {
|
||||
it('should only be available in admin tenant', async () => {
|
||||
await expectRejects(got.get(new URL('/me/custom-data', logtoConsoleUrl)), {
|
||||
await expectRejects(ky.get(new URL('/me/custom-data', logtoConsoleUrl)), {
|
||||
code: 'auth.authorization_header_missing',
|
||||
statusCode: 401,
|
||||
status: 401,
|
||||
});
|
||||
|
||||
// Redirect to UI
|
||||
const response = await got.get(new URL('/me/custom-data', logtoUrl));
|
||||
expect(response.statusCode).toBe(200);
|
||||
expect(response.headers['content-type']?.startsWith('text/html;')).toBeTruthy();
|
||||
const response = await ky.get(new URL('/me/custom-data', logtoUrl));
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get('content-type')?.startsWith('text/html;')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should only recognize the access token with correct resource and scope', async () => {
|
||||
const { id, client } = await createUserWithAllRolesAndSignInToClient();
|
||||
|
||||
await expectRejects(
|
||||
got.get(logtoConsoleUrl + '/me/custom-data', {
|
||||
ky.get(logtoConsoleUrl + '/me/custom-data', {
|
||||
headers: { authorization: `Bearer ${await client.getAccessToken(resourceDefault)}` },
|
||||
}),
|
||||
{
|
||||
code: 'auth.unauthorized',
|
||||
statusCode: 401,
|
||||
status: 401,
|
||||
}
|
||||
);
|
||||
|
||||
await expect(
|
||||
got.get(logtoConsoleUrl + '/me/custom-data', {
|
||||
ky.get(logtoConsoleUrl + '/me/custom-data', {
|
||||
headers: { authorization: `Bearer ${await client.getAccessToken(resourceMe)}` },
|
||||
})
|
||||
).resolves.toHaveProperty('statusCode', 200);
|
||||
).resolves.toHaveProperty('status', 200);
|
||||
|
||||
await deleteUser(id);
|
||||
});
|
||||
|
@ -48,14 +48,14 @@ describe('me', () => {
|
|||
const { id, client } = await createUserWithAllRolesAndSignInToClient();
|
||||
const headers = { authorization: `Bearer ${await client.getAccessToken(resourceMe)}` };
|
||||
|
||||
const data = await got
|
||||
const data = await ky
|
||||
.get(logtoConsoleUrl + '/me/custom-data', { headers })
|
||||
.json<Record<string, unknown>>();
|
||||
const newData = await got
|
||||
const newData = await ky
|
||||
.patch(logtoConsoleUrl + '/me/custom-data', { headers, json: { foo: 'bar' } })
|
||||
.json();
|
||||
.json<Record<string, unknown>>();
|
||||
|
||||
expect({ ...data, foo: 'bar' }).toStrictEqual(newData);
|
||||
expect({ ...data, foo: 'bar' }).toStrictEqual({ ...newData });
|
||||
|
||||
await deleteUser(id);
|
||||
});
|
||||
|
|
|
@ -1,32 +1,30 @@
|
|||
import { demoAppApplicationId } from '@logto/schemas';
|
||||
import { trySafe } from '@silverhand/essentials';
|
||||
import { HTTPError, type Headers, got } from 'got';
|
||||
import ky, { HTTPError } from 'ky';
|
||||
import { type KyHeadersInit } from 'node_modules/ky/distribution/types/options.js';
|
||||
|
||||
import { logtoUrl } from '#src/constants.js';
|
||||
|
||||
describe('content-type: application/json compatibility', () => {
|
||||
const api = got.extend({
|
||||
const api = ky.extend({
|
||||
prefixUrl: new URL('/oidc', logtoUrl),
|
||||
});
|
||||
|
||||
const expectErrorMessageForPayload = async (
|
||||
payload: Record<string, unknown>,
|
||||
errorMessage: string,
|
||||
headers: Headers = {}
|
||||
headers: KyHeadersInit = {}
|
||||
) => {
|
||||
return trySafe(
|
||||
api.post('token', {
|
||||
headers,
|
||||
json: payload,
|
||||
}),
|
||||
(error) => {
|
||||
async (error) => {
|
||||
if (!(error instanceof HTTPError)) {
|
||||
throw new TypeError('Error is not a HTTPError instance.');
|
||||
}
|
||||
expect(JSON.parse(String(error.response.body))).toHaveProperty(
|
||||
'error_description',
|
||||
errorMessage
|
||||
);
|
||||
expect(await error.response.json()).toHaveProperty('error_description', errorMessage);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
|
@ -4,7 +4,6 @@ import { fetchTokenByRefreshToken } from '@logto/js';
|
|||
import { InteractionEvent, type Resource, RoleType } from '@logto/schemas';
|
||||
import { assert } from '@silverhand/essentials';
|
||||
import { createRemoteJWKSet, jwtVerify } from 'jose';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
import { createResource, deleteResource, deleteUser, putInteraction } from '#src/api/index.js';
|
||||
import { assignUsersToRole, createRole, deleteRole } from '#src/api/role.js';
|
||||
|
|
|
@ -4,8 +4,8 @@ import { decodeAccessToken } from '@logto/js';
|
|||
import { type LogtoConfig, Prompt, PersistKey } from '@logto/node';
|
||||
import { GrantType, InteractionEvent, demoAppApplicationId } from '@logto/schemas';
|
||||
import { isKeyInObject, removeUndefinedKeys } from '@silverhand/essentials';
|
||||
import { HTTPError, got } from 'got';
|
||||
import { createRemoteJWKSet, jwtVerify } from 'jose';
|
||||
import ky, { HTTPError } from 'ky';
|
||||
|
||||
import { putInteraction } from '#src/api/index.js';
|
||||
import MockClient, { defaultConfig } from '#src/client/index.js';
|
||||
|
@ -19,7 +19,7 @@ import { generateUsername, generatePassword } from '#src/utils.js';
|
|||
/** A helper class to simplify the test on grant errors. */
|
||||
class GrantError extends Error {
|
||||
constructor(
|
||||
public readonly statusCode: number,
|
||||
public readonly status: number,
|
||||
public readonly body: unknown
|
||||
) {
|
||||
super();
|
||||
|
@ -27,9 +27,9 @@ class GrantError extends Error {
|
|||
}
|
||||
|
||||
/** Create a grant error matcher that matches certain elements of the error response. */
|
||||
const grantErrorContaining = (code: string, description: string, statusCode = 400) =>
|
||||
const grantErrorContaining = (code: string, description: string, status = 400) =>
|
||||
new GrantError(
|
||||
statusCode,
|
||||
status,
|
||||
expect.objectContaining({
|
||||
code,
|
||||
error_description: description,
|
||||
|
@ -49,15 +49,20 @@ class MockOrganizationClient extends MockClient {
|
|||
async fetchOrganizationToken(organizationId?: string, scopes?: string[]) {
|
||||
const refreshToken = await this.getRefreshToken();
|
||||
try {
|
||||
const json = await got
|
||||
const json = await ky
|
||||
.post(`${this.config.endpoint}/oidc/token`, {
|
||||
form: removeUndefinedKeys({
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
},
|
||||
body: new URLSearchParams(
|
||||
removeUndefinedKeys({
|
||||
grant_type: GrantType.RefreshToken,
|
||||
client_id: this.config.appId,
|
||||
refresh_token: refreshToken,
|
||||
refresh_token: refreshToken ?? undefined,
|
||||
organization_id: organizationId,
|
||||
scope: scopes?.join(' '),
|
||||
}),
|
||||
})
|
||||
),
|
||||
})
|
||||
.json();
|
||||
if (isKeyInObject(json, 'refresh_token')) {
|
||||
|
@ -66,7 +71,9 @@ class MockOrganizationClient extends MockClient {
|
|||
return json;
|
||||
} catch (error) {
|
||||
if (error instanceof HTTPError) {
|
||||
throw new GrantError(error.response.statusCode, JSON.parse(String(error.response.body)));
|
||||
const json: unknown = JSON.parse(await error.response.text());
|
||||
console.error('HTTPError:', error.response.status, JSON.stringify(json, undefined, 2));
|
||||
throw new GrantError(error.response.status, json);
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import assert from 'node:assert';
|
|||
|
||||
import { ConnectorType } from '@logto/connector-kit';
|
||||
import { generateStandardId } from '@logto/shared';
|
||||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import { createUser } from '#src/api/admin-user.js';
|
||||
import { clearConnectorsByTypes, setEmailConnector } from '#src/helpers/connector.js';
|
||||
|
@ -11,12 +11,10 @@ import { OrganizationApiTest, OrganizationInvitationApiTest } from '#src/helpers
|
|||
|
||||
const randomId = () => generateStandardId(4);
|
||||
|
||||
const expectErrorResponse = (error: unknown, status: number, code: string) => {
|
||||
const expectErrorResponse = async (error: unknown, statusCode: number, code: string) => {
|
||||
assert(error instanceof HTTPError);
|
||||
const { statusCode, body: raw } = error.response;
|
||||
const body: unknown = JSON.parse(String(raw));
|
||||
expect(statusCode).toBe(status);
|
||||
expect(body).toMatchObject({ code });
|
||||
expect(error.response.status).toBe(statusCode);
|
||||
expect(await error.response.json()).toMatchObject({ code });
|
||||
};
|
||||
|
||||
describe('organization invitation creation', () => {
|
||||
|
@ -105,7 +103,7 @@ describe('organization invitation creation', () => {
|
|||
})
|
||||
.catch((error: unknown) => error);
|
||||
|
||||
expectErrorResponse(error, 501, 'connector.not_found');
|
||||
await expectErrorResponse(error, 501, 'connector.not_found');
|
||||
});
|
||||
|
||||
it('should not be able to create invitations with the same email', async () => {
|
||||
|
@ -124,7 +122,7 @@ describe('organization invitation creation', () => {
|
|||
})
|
||||
.catch((error: unknown) => error);
|
||||
|
||||
expectErrorResponse(error, 422, 'entity.unique_integrity_violation');
|
||||
await expectErrorResponse(error, 422, 'entity.unique_integrity_violation');
|
||||
});
|
||||
|
||||
it('should be able to create invitations with the same email for different organizations', async () => {
|
||||
|
@ -157,7 +155,7 @@ describe('organization invitation creation', () => {
|
|||
})
|
||||
.catch((error: unknown) => error);
|
||||
|
||||
expectErrorResponse(error, 400, 'request.invalid_input');
|
||||
await expectErrorResponse(error, 400, 'request.invalid_input');
|
||||
});
|
||||
|
||||
it('should not be able to create invitations if the invitee is already a member of the organization', async () => {
|
||||
|
@ -174,7 +172,7 @@ describe('organization invitation creation', () => {
|
|||
})
|
||||
.catch((error: unknown) => error);
|
||||
|
||||
expectErrorResponse(error, 422, 'request.invalid_input');
|
||||
await expectErrorResponse(error, 422, 'request.invalid_input');
|
||||
});
|
||||
|
||||
it('should not be able to create invitations with an invalid email', async () => {
|
||||
|
@ -187,7 +185,7 @@ describe('organization invitation creation', () => {
|
|||
})
|
||||
.catch((error: unknown) => error);
|
||||
|
||||
expectErrorResponse(error, 400, 'guard.invalid_input');
|
||||
await expectErrorResponse(error, 400, 'guard.invalid_input');
|
||||
});
|
||||
|
||||
it('should not be able to create invitations with an invalid organization id', async () => {
|
||||
|
@ -199,7 +197,7 @@ describe('organization invitation creation', () => {
|
|||
})
|
||||
.catch((error: unknown) => error);
|
||||
|
||||
expectErrorResponse(error, 422, 'entity.relation_foreign_key_not_found');
|
||||
await expectErrorResponse(error, 422, 'entity.relation_foreign_key_not_found');
|
||||
});
|
||||
|
||||
it('should be able to create invitations with organization role ids', async () => {
|
||||
|
|
|
@ -2,19 +2,17 @@ import assert from 'node:assert';
|
|||
|
||||
import { OrganizationInvitationStatus } from '@logto/schemas';
|
||||
import { generateStandardId } from '@logto/shared';
|
||||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import { OrganizationApiTest, OrganizationInvitationApiTest } from '#src/helpers/organization.js';
|
||||
import { UserApiTest } from '#src/helpers/user.js';
|
||||
|
||||
const randomId = () => generateStandardId(4);
|
||||
|
||||
const expectErrorResponse = (error: unknown, status: number, code: string) => {
|
||||
const expectErrorResponse = async (error: unknown, statusCode: number, code: string) => {
|
||||
assert(error instanceof HTTPError);
|
||||
const { statusCode, body: raw } = error.response;
|
||||
const body: unknown = JSON.parse(String(raw));
|
||||
expect(statusCode).toBe(status);
|
||||
expect(body).toMatchObject({ code });
|
||||
expect(error.response.status).toBe(statusCode);
|
||||
expect(await error.response.json()).toMatchObject({ code });
|
||||
};
|
||||
|
||||
describe('organization invitation status update', () => {
|
||||
|
@ -50,7 +48,7 @@ describe('organization invitation status update', () => {
|
|||
const error = await invitationApi
|
||||
.updateStatus(invitation.id, OrganizationInvitationStatus.Accepted)
|
||||
.catch((error: unknown) => error);
|
||||
expectErrorResponse(error, 422, 'request.invalid_input');
|
||||
await expectErrorResponse(error, 422, 'request.invalid_input');
|
||||
});
|
||||
|
||||
it('should be able to accept an invitation', async () => {
|
||||
|
@ -130,7 +128,7 @@ describe('organization invitation status update', () => {
|
|||
.updateStatus(invitation.id, OrganizationInvitationStatus.Accepted, user.id)
|
||||
.catch((error: unknown) => error);
|
||||
|
||||
expectErrorResponse(error, 422, 'request.invalid_input');
|
||||
await expectErrorResponse(error, 422, 'request.invalid_input');
|
||||
});
|
||||
|
||||
it('should not be able to accept an invitation with an invalid user id', async () => {
|
||||
|
@ -146,7 +144,7 @@ describe('organization invitation status update', () => {
|
|||
.updateStatus(invitation.id, OrganizationInvitationStatus.Accepted, 'invalid')
|
||||
.catch((error: unknown) => error);
|
||||
|
||||
expectErrorResponse(error, 404, 'entity.not_found');
|
||||
await expectErrorResponse(error, 404, 'entity.not_found');
|
||||
});
|
||||
|
||||
it('should not be able to update the status of an ended invitation', async () => {
|
||||
|
@ -164,6 +162,6 @@ describe('organization invitation status update', () => {
|
|||
.updateStatus(invitation.id, OrganizationInvitationStatus.Accepted)
|
||||
.catch((error: unknown) => error);
|
||||
|
||||
expectErrorResponse(error, 422, 'request.invalid_input');
|
||||
await expectErrorResponse(error, 422, 'request.invalid_input');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@ import assert from 'node:assert';
|
|||
|
||||
import { generateStandardId } from '@logto/shared';
|
||||
import { isKeyInObject, pick } from '@silverhand/essentials';
|
||||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import { OrganizationRoleApiTest, OrganizationScopeApiTest } from '#src/helpers/organization.js';
|
||||
|
||||
|
@ -25,9 +25,8 @@ describe('organization role APIs', () => {
|
|||
|
||||
assert(response instanceof HTTPError);
|
||||
|
||||
const { statusCode, body: raw } = response.response;
|
||||
const body: unknown = JSON.parse(String(raw));
|
||||
expect(statusCode).toBe(422);
|
||||
const body: unknown = await response.response.json();
|
||||
expect(response.response.status).toBe(422);
|
||||
expect(isKeyInObject(body, 'code') && body.code).toBe('entity.unique_integrity_violation');
|
||||
});
|
||||
|
||||
|
@ -97,7 +96,7 @@ describe('organization role APIs', () => {
|
|||
it('should fail when try to get an organization role that does not exist', async () => {
|
||||
const response = await roleApi.get('0').catch((error: unknown) => error);
|
||||
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should be able to update organization role', async () => {
|
||||
|
@ -126,8 +125,8 @@ describe('organization role APIs', () => {
|
|||
.catch((error: unknown) => error);
|
||||
|
||||
assert(response instanceof HTTPError);
|
||||
expect(response.response.statusCode).toBe(422);
|
||||
expect(JSON.parse(String(response.response.body))).toMatchObject(
|
||||
expect(response.response.status).toBe(422);
|
||||
expect(await response.response.json()).toMatchObject(
|
||||
expect.objectContaining({
|
||||
code: 'entity.unique_integrity_violation',
|
||||
})
|
||||
|
@ -138,12 +137,12 @@ describe('organization role APIs', () => {
|
|||
const createdRole = await roleApi.create({ name: 'test' + randomId() });
|
||||
await roleApi.delete(createdRole.id);
|
||||
const response = await roleApi.get(createdRole.id).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should fail when try to delete an organization role that does not exist', async () => {
|
||||
const response = await roleApi.delete('0').catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -212,8 +211,8 @@ describe('organization role APIs', () => {
|
|||
.catch((error: unknown) => error);
|
||||
|
||||
assert(response instanceof HTTPError);
|
||||
expect(response.response.statusCode).toBe(422);
|
||||
expect(JSON.parse(String(response.response.body))).toMatchObject(
|
||||
expect(response.response.status).toBe(422);
|
||||
expect(await response.response.json()).toMatchObject(
|
||||
expect.objectContaining({
|
||||
code: 'entity.relation_foreign_key_not_found',
|
||||
})
|
||||
|
@ -248,7 +247,7 @@ describe('organization role APIs', () => {
|
|||
const response = await roleApi.deleteScope(role.id, '0').catch((error: unknown) => error);
|
||||
|
||||
assert(response instanceof HTTPError);
|
||||
expect(response.response.statusCode).toBe(404);
|
||||
expect(response.response.status).toBe(404);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,7 +2,7 @@ import assert from 'node:assert';
|
|||
|
||||
import { generateStandardId } from '@logto/shared';
|
||||
import { isKeyInObject } from '@silverhand/essentials';
|
||||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import { OrganizationScopeApiTest } from '#src/helpers/organization.js';
|
||||
|
||||
|
@ -22,9 +22,8 @@ describe('organization scope APIs', () => {
|
|||
|
||||
assert(response instanceof HTTPError);
|
||||
|
||||
const { statusCode, body: raw } = response.response;
|
||||
const body: unknown = JSON.parse(String(raw));
|
||||
expect(statusCode).toBe(422);
|
||||
const body: unknown = await response.response.json();
|
||||
expect(response.response.status).toBe(422);
|
||||
expect(isKeyInObject(body, 'code') && body.code).toBe('entity.unique_integrity_violation');
|
||||
});
|
||||
|
||||
|
@ -70,7 +69,7 @@ describe('organization scope APIs', () => {
|
|||
it('should fail when try to get an organization scope that does not exist', async () => {
|
||||
const response = await scopeApi.get('0').catch((error: unknown) => error);
|
||||
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should be able to update organization scope', async () => {
|
||||
|
@ -99,8 +98,8 @@ describe('organization scope APIs', () => {
|
|||
.catch((error: unknown) => error);
|
||||
|
||||
assert(response instanceof HTTPError);
|
||||
expect(response.response.statusCode).toBe(422);
|
||||
expect(JSON.parse(String(response.response.body))).toMatchObject(
|
||||
expect(response.response.status).toBe(422);
|
||||
expect(await response.response.json()).toMatchObject(
|
||||
expect.objectContaining({
|
||||
code: 'entity.unique_integrity_violation',
|
||||
})
|
||||
|
@ -111,11 +110,11 @@ describe('organization scope APIs', () => {
|
|||
const createdScope = await scopeApi.create({ name: 'test' + randomId() });
|
||||
await scopeApi.delete(createdScope.id);
|
||||
const response = await scopeApi.get(createdScope.id).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should fail when try to delete an organization scope that does not exist', async () => {
|
||||
const response = await scopeApi.delete('0').catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import assert from 'node:assert';
|
||||
|
||||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import { OrganizationApiTest } from '#src/helpers/organization.js';
|
||||
import { UserApiTest } from '#src/helpers/user.js';
|
||||
|
@ -95,14 +95,14 @@ describe('organization user APIs', () => {
|
|||
const response = await organizationApi
|
||||
.addUsers(organization.id, [])
|
||||
.catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(400);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(400);
|
||||
});
|
||||
|
||||
it('should fail when try to add user to an organization that does not exist', async () => {
|
||||
const response = await organizationApi.addUsers('0', ['0']).catch((error: unknown) => error);
|
||||
assert(response instanceof HTTPError);
|
||||
expect(response.response.statusCode).toBe(422);
|
||||
expect(JSON.parse(String(response.response.body))).toMatchObject(
|
||||
expect(response.response.status).toBe(422);
|
||||
expect(await response.response.json()).toMatchObject(
|
||||
expect.objectContaining({ code: 'entity.relation_foreign_key_not_found' })
|
||||
);
|
||||
});
|
||||
|
@ -120,7 +120,7 @@ describe('organization user APIs', () => {
|
|||
it('should fail when try to delete user from an organization that does not exist', async () => {
|
||||
const response = await organizationApi.deleteUser('0', '0').catch((error: unknown) => error);
|
||||
assert(response instanceof HTTPError);
|
||||
expect(response.response.statusCode).toBe(404);
|
||||
expect(response.response.status).toBe(404);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -146,8 +146,8 @@ describe('organization user APIs', () => {
|
|||
.catch((error: unknown) => error);
|
||||
|
||||
assert(response instanceof HTTPError);
|
||||
expect(response.response.statusCode).toBe(422);
|
||||
expect(JSON.parse(String(response.response.body))).toMatchObject(
|
||||
expect(response.response.status).toBe(422);
|
||||
expect(await response.response.json()).toMatchObject(
|
||||
expect.objectContaining({ code: 'organization.require_membership' })
|
||||
);
|
||||
|
||||
|
@ -244,7 +244,7 @@ describe('organization user APIs', () => {
|
|||
const response = await organizationApi
|
||||
.getUserRoles(organization.id, user.id)
|
||||
.catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(422); // Require membership
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(422); // Require membership
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import { OrganizationApiTest } from '#src/helpers/organization.js';
|
||||
import { UserApiTest } from '#src/helpers/user.js';
|
||||
|
@ -95,7 +95,7 @@ describe('organization APIs', () => {
|
|||
it('should fail when try to get an organization that does not exist', async () => {
|
||||
const response = await organizationApi.get('0').catch((error: unknown) => error);
|
||||
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should be able to update organization', async () => {
|
||||
|
@ -117,11 +117,11 @@ describe('organization APIs', () => {
|
|||
const response = await organizationApi
|
||||
.get(createdOrganization.id)
|
||||
.catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should fail when try to delete an organization that does not exist', async () => {
|
||||
const response = await organizationApi.delete('0').catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import assert from 'node:assert';
|
||||
|
||||
import { defaultManagementApi } from '@logto/schemas';
|
||||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import { createResource } from '#src/api/index.js';
|
||||
import { createScope, deleteScope, getScopes, updateScope } from '#src/api/scope.js';
|
||||
|
@ -32,28 +32,28 @@ describe('scopes', () => {
|
|||
const response = await createScope(resource.id, createdScope.name).catch(
|
||||
(error: unknown) => error
|
||||
);
|
||||
expect(response instanceof HTTPError && response.response.statusCode === 422).toBe(true);
|
||||
expect(response instanceof HTTPError && response.response.status === 422).toBe(true);
|
||||
});
|
||||
|
||||
it('should return 404 when create scope with invalid resource id', async () => {
|
||||
const response = await createScope('invalid_resource_id', 'invalid_scope_name').catch(
|
||||
(error: unknown) => error
|
||||
);
|
||||
expect(response instanceof HTTPError && response.response.statusCode === 404).toBe(true);
|
||||
expect(response instanceof HTTPError && response.response.status === 404).toBe(true);
|
||||
});
|
||||
|
||||
it('should return 400 if scope name is empty', async () => {
|
||||
const resource = await createResource();
|
||||
|
||||
const response = await createScope(resource.id, '').catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode === 400).toBe(true);
|
||||
expect(response instanceof HTTPError && response.response.status === 400).toBe(true);
|
||||
});
|
||||
|
||||
it('should return 400 if scope name has empty space', async () => {
|
||||
const resource = await createResource();
|
||||
|
||||
const response = await createScope(resource.id, 'scope id').catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode === 400).toBe(true);
|
||||
expect(response instanceof HTTPError && response.response.status === 400).toBe(true);
|
||||
});
|
||||
|
||||
it('should update scope successfully', async () => {
|
||||
|
@ -82,7 +82,7 @@ describe('scopes', () => {
|
|||
const response = await updateScope(resource.id, createdScope2.id, {
|
||||
name: createdScope.name,
|
||||
}).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode === 422).toBe(true);
|
||||
expect(response instanceof HTTPError && response.response.status === 422).toBe(true);
|
||||
});
|
||||
|
||||
it('should return 400 if update scope name that has empty space', async () => {
|
||||
|
@ -93,7 +93,7 @@ describe('scopes', () => {
|
|||
}).catch((error: unknown) => error);
|
||||
|
||||
assert(response instanceof HTTPError);
|
||||
expect(response.response.statusCode).toBe(400);
|
||||
expect(response.response.status).toBe(400);
|
||||
});
|
||||
|
||||
it('should return 404 when update scope with invalid resource id', async () => {
|
||||
|
@ -101,7 +101,7 @@ describe('scopes', () => {
|
|||
name: 'scope',
|
||||
}).catch((error: unknown) => error);
|
||||
|
||||
expect(response instanceof HTTPError && response.response.statusCode === 404).toBe(true);
|
||||
expect(response instanceof HTTPError && response.response.status === 404).toBe(true);
|
||||
});
|
||||
|
||||
it('should return 404 when update scope with invalid scope id', async () => {
|
||||
|
@ -111,7 +111,7 @@ describe('scopes', () => {
|
|||
name: 'scope',
|
||||
}).catch((error: unknown) => error);
|
||||
|
||||
expect(response instanceof HTTPError && response.response.statusCode === 404).toBe(true);
|
||||
expect(response instanceof HTTPError && response.response.status === 404).toBe(true);
|
||||
});
|
||||
|
||||
it('should delete scope successfully', async () => {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { defaultManagementApi } from '@logto/schemas';
|
||||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import {
|
||||
createResource,
|
||||
|
@ -21,7 +21,7 @@ describe('admin console api resources', () => {
|
|||
|
||||
it('should return 404 if resource not found', async () => {
|
||||
const response = await getResource('not_found').catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode === 404).toBe(true);
|
||||
expect(response instanceof HTTPError && response.response.status === 404).toBe(true);
|
||||
});
|
||||
|
||||
it('should create api resource successfully', async () => {
|
||||
|
@ -49,7 +49,7 @@ describe('admin console api resources', () => {
|
|||
const resourceName2 = generateResourceName();
|
||||
await expectRejects(createResource(resourceName2, resourceIndicator), {
|
||||
code: 'resource.resource_identifier_in_use',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -93,7 +93,7 @@ describe('admin console api resources', () => {
|
|||
name: 123,
|
||||
}).catch((error: unknown) => error);
|
||||
|
||||
expect(response instanceof HTTPError && response.response.statusCode === 400).toBe(true);
|
||||
expect(response instanceof HTTPError && response.response.status === 400).toBe(true);
|
||||
});
|
||||
|
||||
it('should not update api resource indicator', async () => {
|
||||
|
@ -119,19 +119,19 @@ describe('admin console api resources', () => {
|
|||
await deleteResource(createdResource.id);
|
||||
|
||||
const response = await getResource(createdResource.id).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode === 404).toBe(true);
|
||||
expect(response instanceof HTTPError && response.response.status === 404).toBe(true);
|
||||
});
|
||||
|
||||
it('should throw when deleting management api resource', async () => {
|
||||
const response = await deleteResource(defaultManagementApi.resource.id).catch(
|
||||
(error: unknown) => error
|
||||
);
|
||||
expect(response instanceof HTTPError && response.response.statusCode === 400).toBe(true);
|
||||
expect(response instanceof HTTPError && response.response.status === 400).toBe(true);
|
||||
});
|
||||
|
||||
it('should throw 404 when delete api resource not found', async () => {
|
||||
const response = await deleteResource('dummy_id').catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode === 404).toBe(true);
|
||||
expect(response instanceof HTTPError && response.response.status === 404).toBe(true);
|
||||
});
|
||||
|
||||
it('be able to set only one default api resource', async () => {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { ApplicationType, RoleType } from '@logto/schemas';
|
||||
import { generateStandardId } from '@logto/shared';
|
||||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import { assignRolesToApplication, createApplication } from '#src/api/index.js';
|
||||
import {
|
||||
|
@ -27,7 +27,7 @@ describe('roles applications', () => {
|
|||
|
||||
it('should return 404 if role not found', async () => {
|
||||
const response = await getRoleApplications('not-found').catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should assign applications to role successfully', async () => {
|
||||
|
@ -52,13 +52,13 @@ describe('roles applications', () => {
|
|||
it('should fail when try to assign empty applications', async () => {
|
||||
const role = await createRole({ type: RoleType.MachineToMachine });
|
||||
const response = await assignApplicationsToRole([], role.id).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(400);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(400);
|
||||
});
|
||||
|
||||
it('should fail with invalid application input', async () => {
|
||||
const role = await createRole({ type: RoleType.MachineToMachine });
|
||||
const response = await assignApplicationsToRole([''], role.id).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(400);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(400);
|
||||
});
|
||||
|
||||
it('should fail if role not found', async () => {
|
||||
|
@ -66,7 +66,7 @@ describe('roles applications', () => {
|
|||
const response = await assignApplicationsToRole([m2mApp.id], 'not-found').catch(
|
||||
(error: unknown) => error
|
||||
);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should fail if application not found', async () => {
|
||||
|
@ -74,7 +74,7 @@ describe('roles applications', () => {
|
|||
const response = await assignApplicationsToRole(['not-found'], role.id).catch(
|
||||
(error: unknown) => error
|
||||
);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should remove application from role successfully', async () => {
|
||||
|
@ -95,7 +95,7 @@ describe('roles applications', () => {
|
|||
const response = await deleteApplicationFromRole(m2mApp.id, 'not-found').catch(
|
||||
(error: unknown) => error
|
||||
);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should fail if application not found when trying to remove application from role', async () => {
|
||||
|
@ -103,7 +103,7 @@ describe('roles applications', () => {
|
|||
const response = await deleteApplicationFromRole('not-found', role.id).catch(
|
||||
(error: unknown) => error
|
||||
);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
// This case tests GET operation on roles and filter by `type` parameter and `search` parameter.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { defaultManagementApi } from '@logto/schemas';
|
||||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import { createResource } from '#src/api/index.js';
|
||||
import {
|
||||
|
@ -24,7 +24,7 @@ describe('roles scopes', () => {
|
|||
|
||||
it('should return 404 if role not found', async () => {
|
||||
const response = await getRoleScopes('not-found').catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should return empty if role has no scopes', async () => {
|
||||
|
@ -46,13 +46,13 @@ describe('roles scopes', () => {
|
|||
it('should fail when try to assign empty scopes', async () => {
|
||||
const role = await createRole({});
|
||||
const response = await assignScopesToRole([], role.id).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(400);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(400);
|
||||
});
|
||||
|
||||
it('should fail with invalid scope input', async () => {
|
||||
const role = await createRole({});
|
||||
const response = await assignScopesToRole([''], role.id).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(400);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(400);
|
||||
});
|
||||
|
||||
it('should fail if role not found', async () => {
|
||||
|
@ -61,7 +61,7 @@ describe('roles scopes', () => {
|
|||
const response = await assignScopesToRole([scope.id], 'not-found').catch(
|
||||
(error: unknown) => error
|
||||
);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should fail if scope not found', async () => {
|
||||
|
@ -69,7 +69,7 @@ describe('roles scopes', () => {
|
|||
const response = await assignScopesToRole(['not-found'], role.id).catch(
|
||||
(error: unknown) => error
|
||||
);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should fail if scope already assigned to role', async () => {
|
||||
|
@ -81,7 +81,7 @@ describe('roles scopes', () => {
|
|||
const response = await assignScopesToRole([scope1.id, scope2.id], role.id).catch(
|
||||
(error: unknown) => error
|
||||
);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(422);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(422);
|
||||
});
|
||||
|
||||
it('should fail if try to assign management API scope(s) to user role', async () => {
|
||||
|
@ -91,7 +91,7 @@ describe('roles scopes', () => {
|
|||
[defaultManagementApi.scopes[0]!.id],
|
||||
userRole.id
|
||||
).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(400);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(400);
|
||||
});
|
||||
|
||||
it('should remove scope from role successfully', async () => {
|
||||
|
@ -113,7 +113,7 @@ describe('roles scopes', () => {
|
|||
const resource = await createResource();
|
||||
const scope = await createScope(resource.id);
|
||||
const response = await deleteScopeFromRole(scope.id, role.id).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should fail when try to remove scope from role that is not found', async () => {
|
||||
|
@ -122,7 +122,7 @@ describe('roles scopes', () => {
|
|||
const response = await deleteScopeFromRole(scope.id, 'not-found').catch(
|
||||
(error: unknown) => error
|
||||
);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should fail when try to assign a scope to an internal role', async () => {
|
||||
|
@ -132,6 +132,6 @@ describe('roles scopes', () => {
|
|||
(error: unknown) => error
|
||||
);
|
||||
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(403);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(403);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { defaultManagementApi } from '@logto/schemas';
|
||||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import { createResource } from '#src/api/resource.js';
|
||||
import {
|
||||
|
@ -49,13 +49,13 @@ describe('roles', () => {
|
|||
const { name } = await createRole({});
|
||||
|
||||
const response = await createRole({ name }).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(422);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(422);
|
||||
});
|
||||
|
||||
it('should fail when try to create an internal role', async () => {
|
||||
const response = await createRole({ name: '#internal:foo' }).catch((error: unknown) => error);
|
||||
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(403);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(403);
|
||||
});
|
||||
|
||||
it('should fail when try to create role with management API scope(s)', async () => {
|
||||
|
@ -63,7 +63,7 @@ describe('roles', () => {
|
|||
(error: unknown) => error
|
||||
);
|
||||
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(400);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(400);
|
||||
});
|
||||
|
||||
it('should get role detail successfully', async () => {
|
||||
|
@ -77,7 +77,7 @@ describe('roles', () => {
|
|||
it('should return 404 if role does not exist', async () => {
|
||||
const response = await getRole('non_existent_role').catch((error: unknown) => error);
|
||||
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should update role details successfully', async () => {
|
||||
|
@ -103,14 +103,14 @@ describe('roles', () => {
|
|||
const response = await updateRole(role2.id, {
|
||||
name: role1.name,
|
||||
}).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(422);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(422);
|
||||
});
|
||||
|
||||
it('should fail when update a non-existent role', async () => {
|
||||
const response = await updateRole('non_existent_role', {
|
||||
name: 'new_name',
|
||||
}).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should fail when try to update an internal role', async () => {
|
||||
|
@ -120,7 +120,7 @@ describe('roles', () => {
|
|||
name: '#internal:foo',
|
||||
}).catch((error: unknown) => error);
|
||||
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(403);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(403);
|
||||
});
|
||||
|
||||
it('should delete role successfully', async () => {
|
||||
|
@ -129,12 +129,12 @@ describe('roles', () => {
|
|||
await deleteRole(role.id);
|
||||
|
||||
const response = await getRole(role.id).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should return 404 if role does not exist', async () => {
|
||||
const response = await deleteRole('non_existent_role').catch((error: unknown) => error);
|
||||
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { RoleType } from '@logto/schemas';
|
||||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import { createUser } from '#src/api/index.js';
|
||||
import {
|
||||
|
@ -28,7 +28,7 @@ describe('roles users', () => {
|
|||
|
||||
it('should return 404 if role not found', async () => {
|
||||
const response = await getRoleUsers('not-found').catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should assign users to role successfully', async () => {
|
||||
|
@ -66,7 +66,7 @@ describe('roles users', () => {
|
|||
const user = await createUser(generateNewUserProfile({}));
|
||||
await expectRejects(assignUsersToRole([user.id], m2mRole.id), {
|
||||
code: 'entity.db_constraint_violated',
|
||||
statusCode: 422,
|
||||
status: 422,
|
||||
});
|
||||
const users = await getRoleUsers(m2mRole.id);
|
||||
|
||||
|
@ -76,13 +76,13 @@ describe('roles users', () => {
|
|||
it('should fail when try to assign empty users', async () => {
|
||||
const role = await createRole({});
|
||||
const response = await assignUsersToRole([], role.id).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(400);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(400);
|
||||
});
|
||||
|
||||
it('should fail with invalid user input', async () => {
|
||||
const role = await createRole({});
|
||||
const response = await assignUsersToRole([''], role.id).catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(400);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(400);
|
||||
});
|
||||
|
||||
it('should fail if role not found', async () => {
|
||||
|
@ -90,7 +90,7 @@ describe('roles users', () => {
|
|||
const response = await assignUsersToRole([user.id], 'not-found').catch(
|
||||
(error: unknown) => error
|
||||
);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should fail if user not found', async () => {
|
||||
|
@ -98,7 +98,7 @@ describe('roles users', () => {
|
|||
const response = await assignUsersToRole(['not-found'], role.id).catch(
|
||||
(error: unknown) => error
|
||||
);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should remove user from role successfully', async () => {
|
||||
|
@ -119,7 +119,7 @@ describe('roles users', () => {
|
|||
const response = await deleteUserFromRole(user.id, 'not-found').catch(
|
||||
(error: unknown) => error
|
||||
);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
|
||||
it('should fail if user not found when trying to remove user from role', async () => {
|
||||
|
@ -127,6 +127,6 @@ describe('roles users', () => {
|
|||
const response = await deleteUserFromRole('not-found', role.id).catch(
|
||||
(error: unknown) => error
|
||||
);
|
||||
expect(response instanceof HTTPError && response.response.statusCode).toBe(404);
|
||||
expect(response instanceof HTTPError && response.response.status).toBe(404);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -45,7 +45,7 @@ describe('admin console sign-in experience', () => {
|
|||
|
||||
await expectRejects(updateSignInExperience(newSignInExperience), {
|
||||
code: 'sign_in_experiences.username_requires_password',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { SsoProviderName } from '@logto/schemas';
|
||||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import {
|
||||
providerNames,
|
||||
|
@ -64,7 +64,7 @@ describe('post sso-connectors', () => {
|
|||
providerName: 'OIDC',
|
||||
connectorName: 'test connector name',
|
||||
}),
|
||||
{ code: 'single_sign_on.duplicate_connector_name', statusCode: 409 }
|
||||
{ code: 'single_sign_on.duplicate_connector_name', status: 409 }
|
||||
);
|
||||
|
||||
await deleteSsoConnectorById(id);
|
||||
|
@ -202,7 +202,7 @@ describe('patch sso-connector by id', () => {
|
|||
patchSsoConnectorById(id2, {
|
||||
connectorName: 'test connector name',
|
||||
}),
|
||||
{ code: 'single_sign_on.duplicate_connector_name', statusCode: 409 }
|
||||
{ code: 'single_sign_on.duplicate_connector_name', status: 409 }
|
||||
);
|
||||
|
||||
await deleteSsoConnectorById(id);
|
||||
|
|
|
@ -9,11 +9,11 @@ const { default: OpenApiSchemaValidator } = Validator;
|
|||
describe('Swagger check', () => {
|
||||
it('should provide a valid swagger.json', async () => {
|
||||
const response = await api.get('swagger.json');
|
||||
expect(response).toHaveProperty('statusCode', 200);
|
||||
expect(response.headers['content-type']).toContain('application/json');
|
||||
expect(response).toHaveProperty('status', 200);
|
||||
expect(response.headers.get('content-type')).toContain('application/json');
|
||||
|
||||
// Use multiple validators to be more confident
|
||||
const object: unknown = JSON.parse(response.body);
|
||||
const object: unknown = await response.json();
|
||||
|
||||
const validator = new OpenApiSchemaValidator({ version: 3 });
|
||||
const result = validator.validate(object as OpenAPI.Document);
|
||||
|
|
|
@ -32,7 +32,7 @@ describe('Generic verification code through management API', () => {
|
|||
it('should create an email verification code on server side', async () => {
|
||||
const payload: RequestVerificationCodePayload = { email: mockEmail };
|
||||
const response = await requestVerificationCode(payload);
|
||||
expect(response.statusCode).toBe(204);
|
||||
expect(response.status).toBe(204);
|
||||
|
||||
const { code, type, address } = await readConnectorMessage('Email');
|
||||
|
||||
|
@ -44,7 +44,7 @@ describe('Generic verification code through management API', () => {
|
|||
it('should create an SMS verification code on server side', async () => {
|
||||
const payload: RequestVerificationCodePayload = { phone: mockPhone };
|
||||
const response = await requestVerificationCode(payload);
|
||||
expect(response.statusCode).toBe(204);
|
||||
expect(response.status).toBe(204);
|
||||
|
||||
const { code, type, phone } = await readConnectorMessage('Sms');
|
||||
|
||||
|
@ -56,7 +56,7 @@ describe('Generic verification code through management API', () => {
|
|||
it('should fail to create a verification code on server side when the email and phone are not provided', async () => {
|
||||
await expectRejects(requestVerificationCode({ username: 'any_string' }), {
|
||||
code: 'guard.invalid_input',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
});
|
||||
|
||||
await expect(readConnectorMessage('Email')).rejects.toThrow();
|
||||
|
@ -68,20 +68,17 @@ describe('Generic verification code through management API', () => {
|
|||
await clearConnectorsByTypes([ConnectorType.Email]);
|
||||
await expectRejects(requestVerificationCode({ email: emailForTestSendCode }), {
|
||||
code: 'connector.not_found',
|
||||
statusCode: 501,
|
||||
status: 501,
|
||||
});
|
||||
|
||||
await expect(
|
||||
verifyVerificationCode({ email: emailForTestSendCode, verificationCode: 'any_string' })
|
||||
).rejects.toMatchObject({
|
||||
response: {
|
||||
statusCode: 400,
|
||||
body: JSON.stringify({
|
||||
message: 'Invalid verification code.',
|
||||
await expectRejects(
|
||||
verifyVerificationCode({ email: emailForTestSendCode, verificationCode: 'any_string' }),
|
||||
{
|
||||
messageIncludes: 'Invalid verification code.',
|
||||
code: 'verification_code.code_mismatch',
|
||||
}),
|
||||
},
|
||||
});
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
|
||||
// Restore the email connector
|
||||
await setEmailConnector();
|
||||
|
@ -92,20 +89,17 @@ describe('Generic verification code through management API', () => {
|
|||
await clearConnectorsByTypes([ConnectorType.Sms]);
|
||||
await expectRejects(requestVerificationCode({ phone: phoneForTestSendCode }), {
|
||||
code: 'connector.not_found',
|
||||
statusCode: 501,
|
||||
status: 501,
|
||||
});
|
||||
|
||||
await expect(
|
||||
verifyVerificationCode({ phone: phoneForTestSendCode, verificationCode: 'any_string' })
|
||||
).rejects.toMatchObject({
|
||||
response: {
|
||||
statusCode: 400,
|
||||
body: JSON.stringify({
|
||||
message: 'Invalid verification code.',
|
||||
await expectRejects(
|
||||
verifyVerificationCode({ phone: phoneForTestSendCode, verificationCode: 'any_string' }),
|
||||
{
|
||||
messageIncludes: 'Invalid verification code.',
|
||||
code: 'verification_code.code_mismatch',
|
||||
}),
|
||||
},
|
||||
});
|
||||
status: 400,
|
||||
}
|
||||
);
|
||||
|
||||
// Restore the SMS connector
|
||||
await setSmsConnector();
|
||||
|
@ -136,7 +130,7 @@ describe('Generic verification code through management API', () => {
|
|||
await readConnectorMessage('Sms');
|
||||
await expectRejects(verifyVerificationCode({ phone: mockPhone, verificationCode: '666' }), {
|
||||
code: 'verification_code.code_mismatch',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -148,7 +142,7 @@ describe('Generic verification code through management API', () => {
|
|||
expect(phoneToGetCode).toEqual(phone);
|
||||
await expectRejects(verifyVerificationCode({ phone: phoneToVerify, verificationCode: code }), {
|
||||
code: 'verification_code.not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -160,7 +154,7 @@ describe('Generic verification code through management API', () => {
|
|||
expect(emailToGetCode).toEqual(address);
|
||||
await expectRejects(verifyVerificationCode({ email: emailToVerify, verificationCode: code }), {
|
||||
code: 'verification_code.not_found',
|
||||
statusCode: 400,
|
||||
status: 400,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { type SignInExperience, type Translation, type SsoConnectorMetadata } from '@logto/schemas';
|
||||
import { HTTPError } from 'got';
|
||||
import { HTTPError } from 'ky';
|
||||
|
||||
import api, { adminTenantApi, authedAdminApi } from '#src/api/api.js';
|
||||
import { updateSignInExperience } from '#src/api/index.js';
|
||||
|
@ -15,7 +15,7 @@ describe('.well-known api', () => {
|
|||
|
||||
it('should not found API route in non-admin tenant', async () => {
|
||||
const response = await api.get('.well-known/endpoints/123').catch((error: unknown) => error);
|
||||
expect(response instanceof HTTPError && response.response.statusCode === 404).toBe(true);
|
||||
expect(response instanceof HTTPError && response.response.status === 404).toBe(true);
|
||||
});
|
||||
|
||||
it('get /.well-known/sign-in-exp for console', async () => {
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
/* Test the sign-in with different password policies. */
|
||||
|
||||
import crypto from 'node:crypto';
|
||||
|
||||
import { ConnectorType, SignInIdentifier } from '@logto/schemas';
|
||||
|
||||
import { updateSignInExperience } from '#src/api/sign-in-experience.js';
|
||||
|
@ -9,8 +7,7 @@ import { demoAppUrl } from '#src/constants.js';
|
|||
import { clearConnectorsByTypes, setEmailConnector } from '#src/helpers/connector.js';
|
||||
import ExpectExperience from '#src/ui-helpers/expect-experience.js';
|
||||
import { setupUsernameAndEmailExperience } from '#src/ui-helpers/index.js';
|
||||
|
||||
const randomString = () => crypto.randomBytes(8).toString('hex');
|
||||
import { randomString } from '#src/utils.js';
|
||||
|
||||
describe('password policy', () => {
|
||||
const username = 'test_' + randomString();
|
||||
|
|
|
@ -122,3 +122,5 @@ export const dcls = <C extends string>(className: C) => `div${cls(className)}` a
|
|||
* ```
|
||||
*/
|
||||
export const generateTestName = () => `test_${generateStandardId(4)}`;
|
||||
|
||||
export const randomString = () => crypto.randomBytes(8).toString('hex');
|
||||
|
|
|
@ -2883,8 +2883,8 @@ importers:
|
|||
specifier: ^4.2.0
|
||||
version: 4.2.0
|
||||
ky:
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
specifier: ^1.2.3
|
||||
version: 1.2.3
|
||||
libphonenumber-js:
|
||||
specifier: ^1.10.51
|
||||
version: 1.10.51
|
||||
|
@ -3535,8 +3535,8 @@ importers:
|
|||
specifier: ^3.7.5
|
||||
version: 3.7.5
|
||||
ky:
|
||||
specifier: ^1.0.0
|
||||
version: 1.0.0
|
||||
specifier: ^1.2.3
|
||||
version: 1.2.3
|
||||
libphonenumber-js:
|
||||
specifier: ^1.10.51
|
||||
version: 1.10.51
|
||||
|
@ -3667,9 +3667,6 @@ importers:
|
|||
expect-puppeteer:
|
||||
specifier: ^10.0.0
|
||||
version: 10.0.0
|
||||
got:
|
||||
specifier: ^14.0.0
|
||||
version: 14.0.0
|
||||
jest:
|
||||
specifier: ^29.7.0
|
||||
version: 29.7.0(@types/node@20.10.4)
|
||||
|
@ -3682,9 +3679,9 @@ importers:
|
|||
jose:
|
||||
specifier: ^5.0.0
|
||||
version: 5.0.1
|
||||
node-fetch:
|
||||
specifier: ^3.3.0
|
||||
version: 3.3.0
|
||||
ky:
|
||||
specifier: ^1.2.3
|
||||
version: 1.2.3
|
||||
openapi-schema-validator:
|
||||
specifier: ^12.1.3
|
||||
version: 12.1.3
|
||||
|
@ -9465,6 +9462,7 @@ packages:
|
|||
/@sindresorhus/is@6.1.0:
|
||||
resolution: {integrity: sha512-BuvU07zq3tQ/2SIgBsEuxKYDyDjC0n7Zir52bpHy2xnBbW81+po43aLFPLbeV3HRAheFbGud1qgcqSYfhtHMAg==}
|
||||
engines: {node: '>=16'}
|
||||
dev: false
|
||||
|
||||
/@sinonjs/commons@2.0.0:
|
||||
resolution: {integrity: sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==}
|
||||
|
@ -9773,6 +9771,7 @@ packages:
|
|||
engines: {node: '>=14.16'}
|
||||
dependencies:
|
||||
defer-to-connect: 2.0.1
|
||||
dev: false
|
||||
|
||||
/@testing-library/dom@9.0.0:
|
||||
resolution: {integrity: sha512-+/TLgKNFsYUshOY/zXsQOk+PlFQK+eyJ9T13IDVNJEi+M+Un7xlJK+FZKkbGSnf0+7E1G6PlDhkSYQ/GFiruBQ==}
|
||||
|
@ -10012,6 +10011,7 @@ packages:
|
|||
|
||||
/@types/http-cache-semantics@4.0.4:
|
||||
resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
|
||||
dev: false
|
||||
|
||||
/@types/http-errors@1.8.2:
|
||||
resolution: {integrity: sha512-EqX+YQxINb+MeXaIqYDASb6U6FCHbWjkj4a1CKDBks3d/QiB2+PqBLyO72vLDgAO1wUI4O+9gweRcQK11bTL/w==}
|
||||
|
@ -11338,6 +11338,7 @@ packages:
|
|||
/cacheable-lookup@7.0.0:
|
||||
resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==}
|
||||
engines: {node: '>=14.16'}
|
||||
dev: false
|
||||
|
||||
/cacheable-request@10.2.14:
|
||||
resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==}
|
||||
|
@ -11350,6 +11351,7 @@ packages:
|
|||
mimic-response: 4.0.0
|
||||
normalize-url: 8.0.0
|
||||
responselike: 3.0.0
|
||||
dev: false
|
||||
|
||||
/call-bind@1.0.2:
|
||||
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
|
||||
|
@ -12168,11 +12170,6 @@ packages:
|
|||
engines: {node: '>=12'}
|
||||
dev: true
|
||||
|
||||
/data-uri-to-buffer@4.0.0:
|
||||
resolution: {integrity: sha512-Vr3mLBA8qWmcuschSLAOogKgQ/Jwxulv3RNE4FXnYWRGujzrRWQI4m12fQqRkwX06C0KanhLr4hK+GydchZsaA==}
|
||||
engines: {node: '>= 12'}
|
||||
dev: true
|
||||
|
||||
/data-uri-to-buffer@5.0.1:
|
||||
resolution: {integrity: sha512-a9l6T1qqDogvvnw0nKlfZzqsyikEBZBClF39V3TFoKhDtGBqHu2HkuomJc02j5zft8zrUaXEuoicLeW54RkzPg==}
|
||||
engines: {node: '>= 14'}
|
||||
|
@ -12263,6 +12260,7 @@ packages:
|
|||
engines: {node: '>=10'}
|
||||
dependencies:
|
||||
mimic-response: 3.1.0
|
||||
dev: false
|
||||
|
||||
/dedent@1.5.1:
|
||||
resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==}
|
||||
|
@ -12307,6 +12305,7 @@ packages:
|
|||
/defer-to-connect@2.0.1:
|
||||
resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
|
||||
engines: {node: '>=10'}
|
||||
dev: false
|
||||
|
||||
/define-lazy-prop@2.0.0:
|
||||
resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
|
||||
|
@ -13506,14 +13505,6 @@ packages:
|
|||
pend: 1.2.0
|
||||
dev: true
|
||||
|
||||
/fetch-blob@3.2.0:
|
||||
resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==}
|
||||
engines: {node: ^12.20 || >= 14.13}
|
||||
dependencies:
|
||||
node-domexception: 1.0.0
|
||||
web-streams-polyfill: 3.2.1
|
||||
dev: true
|
||||
|
||||
/figures@5.0.0:
|
||||
resolution: {integrity: sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==}
|
||||
engines: {node: '>=14'}
|
||||
|
@ -13664,6 +13655,7 @@ packages:
|
|||
/form-data-encoder@4.0.2:
|
||||
resolution: {integrity: sha512-KQVhvhK8ZkWzxKxOr56CPulAhH3dobtuQ4+hNQ+HekH/Wp5gSOafqRAeTphQUJAIk0GBvHZgJ2ZGRWd5kphMuw==}
|
||||
engines: {node: '>= 18'}
|
||||
dev: false
|
||||
|
||||
/form-data@3.0.1:
|
||||
resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==}
|
||||
|
@ -13687,13 +13679,6 @@ packages:
|
|||
engines: {node: '>=0.4.x'}
|
||||
dev: true
|
||||
|
||||
/formdata-polyfill@4.0.10:
|
||||
resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==}
|
||||
engines: {node: '>=12.20.0'}
|
||||
dependencies:
|
||||
fetch-blob: 3.2.0
|
||||
dev: true
|
||||
|
||||
/formidable@2.0.1:
|
||||
resolution: {integrity: sha512-rjTMNbp2BpfQShhFbR3Ruk3qk2y9jKpvMW78nJgx8QKtxjDVrwbZG+wvDOmVbifHyOUOQJXxqEy6r0faRrPzTQ==}
|
||||
dependencies:
|
||||
|
@ -14082,6 +14067,7 @@ packages:
|
|||
lowercase-keys: 3.0.0
|
||||
p-cancelable: 4.0.1
|
||||
responselike: 3.0.0
|
||||
dev: false
|
||||
|
||||
/graceful-fs@4.2.10:
|
||||
resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
|
||||
|
@ -14382,6 +14368,7 @@ packages:
|
|||
|
||||
/http-cache-semantics@4.1.1:
|
||||
resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
|
||||
dev: false
|
||||
|
||||
/http-errors@1.4.0:
|
||||
resolution: {integrity: sha512-oLjPqve1tuOl5aRhv8GK5eHpqP1C9fb+Ol+XTLjKfLltE44zdDbEdjPSbU7Ch5rSNsVFqZn97SrMmZLdu1/YMw==}
|
||||
|
@ -14459,6 +14446,7 @@ packages:
|
|||
dependencies:
|
||||
quick-lru: 5.1.1
|
||||
resolve-alpn: 1.2.1
|
||||
dev: false
|
||||
|
||||
/https-proxy-agent@5.0.1:
|
||||
resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
|
||||
|
@ -16225,8 +16213,8 @@ packages:
|
|||
- supports-color
|
||||
dev: false
|
||||
|
||||
/ky@1.0.0:
|
||||
resolution: {integrity: sha512-ptUup6btycj2IXLL/L5Bh/QC9ghYC2k/BsnnSf0ccEap8FpCvJ32FL5s5Potb8NkH6HOTdnVGmyI1wkHZ3mfEQ==}
|
||||
/ky@1.2.3:
|
||||
resolution: {integrity: sha512-2IM3VssHfG2zYz2FsHRUqIp8chhLc9uxDMcK2THxgFfv8pQhnMfN8L0ul+iW4RdBl5AglF8ooPIflRm3yNH0IA==}
|
||||
engines: {node: '>=18'}
|
||||
dev: true
|
||||
|
||||
|
@ -16597,6 +16585,7 @@ packages:
|
|||
/lowercase-keys@3.0.0:
|
||||
resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
dev: false
|
||||
|
||||
/lowlight@1.20.0:
|
||||
resolution: {integrity: sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==}
|
||||
|
@ -17241,10 +17230,12 @@ packages:
|
|||
/mimic-response@3.1.0:
|
||||
resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
|
||||
engines: {node: '>=10'}
|
||||
dev: false
|
||||
|
||||
/mimic-response@4.0.0:
|
||||
resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
dev: false
|
||||
|
||||
/min-indent@1.0.1:
|
||||
resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
|
||||
|
@ -17441,11 +17432,6 @@ packages:
|
|||
resolution: {integrity: sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==}
|
||||
dev: true
|
||||
|
||||
/node-domexception@1.0.0:
|
||||
resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==}
|
||||
engines: {node: '>=10.5.0'}
|
||||
dev: true
|
||||
|
||||
/node-fetch@2.6.7:
|
||||
resolution: {integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==}
|
||||
engines: {node: 4.x || >=6.0.0}
|
||||
|
@ -17470,15 +17456,6 @@ packages:
|
|||
whatwg-url: 5.0.0
|
||||
dev: false
|
||||
|
||||
/node-fetch@3.3.0:
|
||||
resolution: {integrity: sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
dependencies:
|
||||
data-uri-to-buffer: 4.0.0
|
||||
fetch-blob: 3.2.0
|
||||
formdata-polyfill: 4.0.10
|
||||
dev: true
|
||||
|
||||
/node-forge@1.3.1:
|
||||
resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==}
|
||||
engines: {node: '>= 6.13.0'}
|
||||
|
@ -17600,6 +17577,7 @@ packages:
|
|||
/normalize-url@8.0.0:
|
||||
resolution: {integrity: sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==}
|
||||
engines: {node: '>=14.16'}
|
||||
dev: false
|
||||
|
||||
/npm-run-path@4.0.1:
|
||||
resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
|
||||
|
@ -17879,6 +17857,7 @@ packages:
|
|||
/p-cancelable@4.0.1:
|
||||
resolution: {integrity: sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==}
|
||||
engines: {node: '>=14.16'}
|
||||
dev: false
|
||||
|
||||
/p-defer@4.0.0:
|
||||
resolution: {integrity: sha512-Vb3QRvQ0Y5XnF40ZUWW7JfLogicVh/EnA5gBIvKDJoYpeI82+1E3AlB9yOcKFS0AhHrWVnAQO39fbR0G99IVEQ==}
|
||||
|
@ -19499,6 +19478,7 @@ packages:
|
|||
|
||||
/resolve-alpn@1.2.1:
|
||||
resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==}
|
||||
dev: false
|
||||
|
||||
/resolve-cwd@3.0.0:
|
||||
resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
|
||||
|
@ -19560,6 +19540,7 @@ packages:
|
|||
engines: {node: '>=14.16'}
|
||||
dependencies:
|
||||
lowercase-keys: 3.0.0
|
||||
dev: false
|
||||
|
||||
/restore-cursor@4.0.0:
|
||||
resolution: {integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==}
|
||||
|
@ -21591,11 +21572,6 @@ packages:
|
|||
resolution: {integrity: sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==}
|
||||
dev: true
|
||||
|
||||
/web-streams-polyfill@3.2.1:
|
||||
resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==}
|
||||
engines: {node: '>= 8'}
|
||||
dev: true
|
||||
|
||||
/webidl-conversions@3.0.1:
|
||||
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
|
||||
dev: false
|
||||
|
|
Loading…
Reference in a new issue