0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

chore: add integration tests

This commit is contained in:
Gao Sun 2023-05-22 22:04:09 +08:00
parent 92625e5019
commit 9c59102f97
No known key found for this signature in database
GPG key ID: 13EBE123E4773688
4 changed files with 131 additions and 4 deletions

View file

@ -7,12 +7,17 @@ import type {
import { authedAdminApi } from './api.js';
export const createApplication = async (name: string, type: ApplicationType) =>
export const createApplication = async (
name: string,
type: ApplicationType,
rest?: Partial<CreateApplication>
) =>
authedAdminApi
.post('applications', {
json: {
name,
type,
...rest,
},
})
.json<Application>();

View file

@ -0,0 +1,68 @@
import { Prompt } from '@logto/js';
import { ApplicationType, InteractionEvent } from '@logto/schemas';
import { createApplication, deleteApplication, putInteraction } from '#src/api/index.js';
import MockClient from '#src/client/index.js';
import { demoAppRedirectUri } from '#src/constants.js';
import { processSession } from '#src/helpers/client.js';
import { createUserByAdmin } from '#src/helpers/index.js';
import { enableAllPasswordSignInMethods } from '#src/helpers/sign-in-experience.js';
import { generateUsername, generatePassword } from '#src/utils.js';
describe('always issue Refresh Token config', () => {
const username = generateUsername();
const password = generatePassword();
const validateRefreshToken = async (appId: string, redirectUri: string, expectToken: boolean) => {
const client = new MockClient({
appId,
prompt: Prompt.Login,
});
await client.initSession(redirectUri);
await client.successSend(putInteraction, {
event: InteractionEvent.SignIn,
identifier: { username, password },
});
const { redirectTo } = await client.submitInteraction();
await processSession(client, redirectTo);
if (expectToken) {
expect(await client.getRefreshToken()).not.toBeNull();
} else {
expect(await client.getRefreshToken()).toBeNull();
}
};
beforeAll(async () => {
await createUserByAdmin(username, password);
await enableAllPasswordSignInMethods();
});
it('can sign in and fetch Refresh Token without `prompt=consent` when always issue Refresh Token is set', async () => {
const app = await createApplication('Integration test app', ApplicationType.SPA, {
oidcClientMetadata: { redirectUris: [demoAppRedirectUri], postLogoutRedirectUris: [] },
customClientMetadata: { alwaysIssueRefreshToken: true },
});
await validateRefreshToken(app.id, demoAppRedirectUri, true);
await deleteApplication(app.id);
});
it('cannot fetch Refresh Token if alwaysIssueRefreshToken is false and prompt is not consent', async () => {
const app = await createApplication('Integration test app', ApplicationType.SPA, {
oidcClientMetadata: { redirectUris: [demoAppRedirectUri], postLogoutRedirectUris: [] },
customClientMetadata: { alwaysIssueRefreshToken: false },
});
await validateRefreshToken(app.id, demoAppRedirectUri, false);
await deleteApplication(app.id);
});
it('cannot fetch Refresh Token for non-web apps', async () => {
const redirectUri = 'io.logto://callback';
const app = await createApplication('Integration test app', ApplicationType.Native, {
oidcClientMetadata: { redirectUris: [redirectUri], postLogoutRedirectUris: [] },
customClientMetadata: { alwaysIssueRefreshToken: true },
});
await validateRefreshToken(app.id, redirectUri, false);
await deleteApplication(app.id);
});
});

View file

@ -0,0 +1,54 @@
import { demoAppApplicationId } from '@logto/schemas';
import { trySafe } from '@silverhand/essentials';
import { HTTPError, type Headers, got } from 'got';
import { logtoUrl } from '#src/constants.js';
describe('content-type: application/json compatibility', () => {
const api = got.extend({
prefixUrl: new URL('/oidc', logtoUrl),
});
const expectErrorMessageForPayload = async (
payload: Record<string, unknown>,
errorMessage: string,
headers?: Headers
) => {
return trySafe(
api.post('token', {
headers,
json: payload,
}),
(error) => {
if (!(error instanceof HTTPError)) {
return fail('Error is not a HTTPError instance.');
}
expect(JSON.parse(String(error.response.body))).toHaveProperty(
'error_description',
errorMessage
);
}
);
};
it('recognizes `application/json` content-type in OIDC token endpoints', async () => {
await Promise.all([
expectErrorMessageForPayload(
{ client_id: demoAppApplicationId },
"missing required parameter 'grant_type'"
),
expectErrorMessageForPayload(
{ client_id: demoAppApplicationId, grant_type: 'refresh_token' },
"missing required parameter 'refresh_token'"
),
]);
});
it('does not recognize `application/json1` content-type', async () => {
await expectErrorMessageForPayload(
{ client_id: demoAppApplicationId },
'only application/x-www-form-urlencoded content-type bodies are supported on POST /token',
{ 'content-type': 'application/json1' }
);
});
});

View file

@ -26,7 +26,7 @@ describe('get access token', () => {
await enableAllPasswordSignInMethods();
});
it('sign-in and getAccessToken with admin user', async () => {
it('can sign in and getAccessToken with admin user', async () => {
const client = new MockClient({
resources: [defaultManagementApi.resource.indicator],
scopes: [defaultManagementApi.scope.name],
@ -49,7 +49,7 @@ describe('get access token', () => {
void expect(client.getAccessToken('api.foo.com')).rejects.toThrow();
});
it('sign-in and getAccessToken with guest user', async () => {
it('can sign in and getAccessToken with guest user', async () => {
const client = new MockClient({
resources: [defaultManagementApi.resource.indicator],
scopes: [defaultManagementApi.scope.name],
@ -69,7 +69,7 @@ describe('get access token', () => {
);
});
it('sign-in and get multiple Access Token by the same Refresh Token within refreshTokenReuseInterval', async () => {
it('can sign in and get multiple Access Tokens by the same Refresh Token within refreshTokenReuseInterval', async () => {
const client = new MockClient({ resources: [defaultManagementApi.resource.indicator] });
await client.initSession();