0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2025-02-17 22:04:19 -05:00

feat(core): add jwt-customizer test script deployment (#5686)

feat(core): call cloud worker deploy service on custom jwt test

call cloud worker deploy service on custom jwt test
This commit is contained in:
simeng-li 2024-04-14 19:55:02 +08:00 committed by GitHub
parent 63639ad502
commit 568e3dc202
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 86 additions and 28 deletions

View file

@ -1,13 +1,17 @@
import { LogtoJwtTokenKey } from '@logto/schemas'; import {
LogtoJwtTokenKey,
LogtoJwtTokenKeyType,
type JwtCustomizerTestRequestBody,
} from '@logto/schemas';
import { pickDefault } from '@logto/shared/esm'; import { pickDefault } from '@logto/shared/esm';
import { pick } from '@silverhand/essentials'; import { pick } from '@silverhand/essentials';
import Sinon from 'sinon';
import { import {
mockJwtCustomizerConfigForAccessToken, mockJwtCustomizerConfigForAccessToken,
mockJwtCustomizerConfigForClientCredentials, mockJwtCustomizerConfigForClientCredentials,
mockLogtoConfigRows, mockLogtoConfigRows,
} from '#src/__mocks__/index.js'; } from '#src/__mocks__/index.js';
import { mockCloudClient, mockLogtoConfigsLibrary } from '#src/test-utils/mock-libraries.js';
import { MockTenant } from '#src/test-utils/tenant.js'; import { MockTenant } from '#src/test-utils/tenant.js';
import { createRequester } from '#src/utils/test-utils.js'; import { createRequester } from '#src/utils/test-utils.js';
@ -18,20 +22,16 @@ const logtoConfigQueries = {
deleteJwtCustomizer: jest.fn(), deleteJwtCustomizer: jest.fn(),
}; };
const logtoConfigLibraries = {
upsertJwtCustomizer: jest.fn(),
getJwtCustomizer: jest.fn(),
getJwtCustomizers: jest.fn(),
updateJwtCustomizer: jest.fn(),
deployJwtCustomizerScript: jest.fn(),
undeployJwtCustomizerScript: jest.fn(),
};
const settingRoutes = await pickDefault(import('./index.js')); const settingRoutes = await pickDefault(import('./index.js'));
describe('configs JWT customizer routes', () => { describe('configs JWT customizer routes', () => {
const tenantContext = new MockTenant(undefined, { logtoConfigs: logtoConfigQueries }); const tenantContext = new MockTenant(
Sinon.stub(tenantContext, 'logtoConfigs').value(logtoConfigLibraries); undefined,
{ logtoConfigs: logtoConfigQueries },
undefined,
undefined,
mockLogtoConfigsLibrary
);
const routeRequester = createRequester({ const routeRequester = createRequester({
authedRoutes: settingRoutes, authedRoutes: settingRoutes,
@ -48,16 +48,22 @@ describe('configs JWT customizer routes', () => {
rows: [], rows: [],
rowCount: 0, rowCount: 0,
}); });
logtoConfigLibraries.upsertJwtCustomizer.mockResolvedValueOnce( mockLogtoConfigsLibrary.upsertJwtCustomizer.mockResolvedValueOnce(
mockJwtCustomizerConfigForAccessToken mockJwtCustomizerConfigForAccessToken
); );
const response = await routeRequester const response = await routeRequester
.put(`/configs/jwt-customizer/access-token`) .put(`/configs/jwt-customizer/access-token`)
.send(mockJwtCustomizerConfigForAccessToken.value); .send(mockJwtCustomizerConfigForAccessToken.value);
expect(logtoConfigLibraries.upsertJwtCustomizer).toHaveBeenCalled(); expect(mockLogtoConfigsLibrary.deployJwtCustomizerScript).toHaveBeenCalledWith(
tenantContext.cloudConnection,
{
key: LogtoJwtTokenKey.AccessToken,
value: mockJwtCustomizerConfigForAccessToken.value,
}
);
expect(logtoConfigLibraries.upsertJwtCustomizer).toHaveBeenCalledWith( expect(mockLogtoConfigsLibrary.upsertJwtCustomizer).toHaveBeenCalledWith(
LogtoJwtTokenKey.AccessToken, LogtoJwtTokenKey.AccessToken,
mockJwtCustomizerConfigForAccessToken.value mockJwtCustomizerConfigForAccessToken.value
); );
@ -71,13 +77,13 @@ describe('configs JWT customizer routes', () => {
rows: [mockJwtCustomizerConfigForAccessToken], rows: [mockJwtCustomizerConfigForAccessToken],
rowCount: 1, rowCount: 1,
}); });
logtoConfigLibraries.upsertJwtCustomizer.mockResolvedValueOnce( mockLogtoConfigsLibrary.upsertJwtCustomizer.mockResolvedValueOnce(
mockJwtCustomizerConfigForAccessToken mockJwtCustomizerConfigForAccessToken
); );
const response = await routeRequester const response = await routeRequester
.put('/configs/jwt-customizer/access-token') .put('/configs/jwt-customizer/access-token')
.send(mockJwtCustomizerConfigForAccessToken.value); .send(mockJwtCustomizerConfigForAccessToken.value);
expect(logtoConfigLibraries.upsertJwtCustomizer).toHaveBeenCalledWith( expect(mockLogtoConfigsLibrary.upsertJwtCustomizer).toHaveBeenCalledWith(
LogtoJwtTokenKey.AccessToken, LogtoJwtTokenKey.AccessToken,
mockJwtCustomizerConfigForAccessToken.value mockJwtCustomizerConfigForAccessToken.value
); );
@ -86,16 +92,22 @@ describe('configs JWT customizer routes', () => {
}); });
it('PATCH /configs/jwt-customizer/:tokenType should update a record successfully', async () => { it('PATCH /configs/jwt-customizer/:tokenType should update a record successfully', async () => {
logtoConfigLibraries.updateJwtCustomizer.mockResolvedValueOnce( mockLogtoConfigsLibrary.updateJwtCustomizer.mockResolvedValueOnce(
mockJwtCustomizerConfigForAccessToken.value mockJwtCustomizerConfigForAccessToken.value
); );
const response = await routeRequester const response = await routeRequester
.patch('/configs/jwt-customizer/access-token') .patch('/configs/jwt-customizer/access-token')
.send(mockJwtCustomizerConfigForAccessToken.value); .send(mockJwtCustomizerConfigForAccessToken.value);
expect(logtoConfigLibraries.deployJwtCustomizerScript).toHaveBeenCalled(); expect(mockLogtoConfigsLibrary.deployJwtCustomizerScript).toHaveBeenCalledWith(
tenantContext.cloudConnection,
{
key: LogtoJwtTokenKey.AccessToken,
value: mockJwtCustomizerConfigForAccessToken.value,
}
);
expect(logtoConfigLibraries.updateJwtCustomizer).toHaveBeenCalledWith( expect(mockLogtoConfigsLibrary.updateJwtCustomizer).toHaveBeenCalledWith(
LogtoJwtTokenKey.AccessToken, LogtoJwtTokenKey.AccessToken,
mockJwtCustomizerConfigForAccessToken.value mockJwtCustomizerConfigForAccessToken.value
); );
@ -104,7 +116,7 @@ describe('configs JWT customizer routes', () => {
}); });
it('GET /configs/jwt-customizer should return all records', async () => { it('GET /configs/jwt-customizer should return all records', async () => {
logtoConfigLibraries.getJwtCustomizers.mockResolvedValueOnce({ mockLogtoConfigsLibrary.getJwtCustomizers.mockResolvedValueOnce({
[LogtoJwtTokenKey.AccessToken]: mockJwtCustomizerConfigForAccessToken.value, [LogtoJwtTokenKey.AccessToken]: mockJwtCustomizerConfigForAccessToken.value,
[LogtoJwtTokenKey.ClientCredentials]: mockJwtCustomizerConfigForClientCredentials.value, [LogtoJwtTokenKey.ClientCredentials]: mockJwtCustomizerConfigForClientCredentials.value,
}); });
@ -117,7 +129,7 @@ describe('configs JWT customizer routes', () => {
}); });
it('GET /configs/jwt-customizer/:tokenType should return the record', async () => { it('GET /configs/jwt-customizer/:tokenType should return the record', async () => {
logtoConfigLibraries.getJwtCustomizer.mockResolvedValueOnce( mockLogtoConfigsLibrary.getJwtCustomizer.mockResolvedValueOnce(
mockJwtCustomizerConfigForAccessToken.value mockJwtCustomizerConfigForAccessToken.value
); );
const response = await routeRequester.get('/configs/jwt-customizer/access-token'); const response = await routeRequester.get('/configs/jwt-customizer/access-token');
@ -127,7 +139,7 @@ describe('configs JWT customizer routes', () => {
it('DELETE /configs/jwt-customizer/:tokenType should delete the record', async () => { it('DELETE /configs/jwt-customizer/:tokenType should delete the record', async () => {
const response = await routeRequester.delete('/configs/jwt-customizer/client-credentials'); const response = await routeRequester.delete('/configs/jwt-customizer/client-credentials');
expect(logtoConfigLibraries.undeployJwtCustomizerScript).toHaveBeenCalledWith( expect(mockLogtoConfigsLibrary.undeployJwtCustomizerScript).toHaveBeenCalledWith(
tenantContext.cloudConnection, tenantContext.cloudConnection,
LogtoJwtTokenKey.ClientCredentials LogtoJwtTokenKey.ClientCredentials
); );
@ -136,4 +148,35 @@ describe('configs JWT customizer routes', () => {
); );
expect(response.status).toEqual(204); expect(response.status).toEqual(204);
}); });
it('POST /configs/jwt-customizer/test should return 200', async () => {
const cloudConnectionResponse = { success: true };
jest.spyOn(tenantContext.cloudConnection, 'getClient').mockResolvedValue(mockCloudClient);
jest.spyOn(mockCloudClient, 'post').mockResolvedValue(cloudConnectionResponse);
const payload: JwtCustomizerTestRequestBody = {
tokenType: LogtoJwtTokenKeyType.AccessToken,
token: {},
script: mockJwtCustomizerConfigForAccessToken.value.script,
environmentVariables: mockJwtCustomizerConfigForAccessToken.value.environmentVariables,
context: mockJwtCustomizerConfigForAccessToken.value.contextSample,
};
const response = await routeRequester.post('/configs/jwt-customizer/test').send(payload);
expect(mockLogtoConfigsLibrary.deployJwtCustomizerScript).toHaveBeenCalledWith(
tenantContext.cloudConnection,
{
key: LogtoJwtTokenKey.AccessToken,
value: payload,
isTest: true,
}
);
expect(mockCloudClient.post).toHaveBeenCalledWith('/api/services/custom-jwt', {
body: payload,
});
expect(response.status).toEqual(200);
});
}); });

View file

@ -200,7 +200,7 @@ export default function logtoConfigJwtCustomizerRoutes<T extends AuthedRouter>(
} }
); );
if (!EnvSet.values.isCloud) { if (!EnvSet.values.isCloud && !EnvSet.values.isUnitTest) {
return; return;
} }
@ -219,6 +219,16 @@ export default function logtoConfigJwtCustomizerRoutes<T extends AuthedRouter>(
async (ctx, next) => { async (ctx, next) => {
const { body } = ctx.guard; const { body } = ctx.guard;
// Deploy the test script
await deployJwtCustomizerScript(cloudConnection, {
key:
body.tokenType === LogtoJwtTokenKeyType.AccessToken
? LogtoJwtTokenKey.AccessToken
: LogtoJwtTokenKey.ClientCredentials,
value: body,
isTest: true,
});
const client = await cloudConnection.getClient(); const client = await cloudConnection.getClient();
try { try {

View file

@ -1,8 +1,11 @@
import type router from '@logto/cloud/routes';
import Client from '@withtyped/client';
import { type LogtoConfigLibrary } from '#src/libraries/logto-config.js'; import { type LogtoConfigLibrary } from '#src/libraries/logto-config.js';
const { jest } = import.meta; const { jest } = import.meta;
export const mockLogtoConfigsLibrary: LogtoConfigLibrary = { export const mockLogtoConfigsLibrary: jest.Mocked<LogtoConfigLibrary> = {
getCloudConnectionData: jest.fn(), getCloudConnectionData: jest.fn(),
getOidcConfigs: jest.fn(), getOidcConfigs: jest.fn(),
upsertJwtCustomizer: jest.fn(), upsertJwtCustomizer: jest.fn(),
@ -12,3 +15,5 @@ export const mockLogtoConfigsLibrary: LogtoConfigLibrary = {
deployJwtCustomizerScript: jest.fn(), deployJwtCustomizerScript: jest.fn(),
undeployJwtCustomizerScript: jest.fn(), undeployJwtCustomizerScript: jest.fn(),
}; };
export const mockCloudClient = new Client<typeof router>({ baseUrl: 'http://localhost:3001' });

View file

@ -7,8 +7,7 @@ import type { CloudConnectionLibrary } from '#src/libraries/cloud-connection.js'
import { createCloudConnectionLibrary } from '#src/libraries/cloud-connection.js'; import { createCloudConnectionLibrary } from '#src/libraries/cloud-connection.js';
import type { ConnectorLibrary } from '#src/libraries/connector.js'; import type { ConnectorLibrary } from '#src/libraries/connector.js';
import { createConnectorLibrary } from '#src/libraries/connector.js'; import { createConnectorLibrary } from '#src/libraries/connector.js';
import { createLogtoConfigLibrary } from '#src/libraries/logto-config.js'; import { createLogtoConfigLibrary, type LogtoConfigLibrary } from '#src/libraries/logto-config.js';
import { type LogtoConfigLibrary } from '#src/libraries/logto-config.js';
import Libraries from '#src/tenants/Libraries.js'; import Libraries from '#src/tenants/Libraries.js';
import Queries from '#src/tenants/Queries.js'; import Queries from '#src/tenants/Queries.js';
import type TenantContext from '#src/tenants/TenantContext.js'; import type TenantContext from '#src/tenants/TenantContext.js';
@ -78,6 +77,7 @@ export class MockTenant implements TenantContext {
logtoConfigsOverride?: Partial<LogtoConfigLibrary> logtoConfigsOverride?: Partial<LogtoConfigLibrary>
) { ) {
this.queries = new MockQueries(queriesOverride); this.queries = new MockQueries(queriesOverride);
this.logtoConfigs = { ...createLogtoConfigLibrary(this.queries), ...logtoConfigsOverride }; this.logtoConfigs = { ...createLogtoConfigLibrary(this.queries), ...logtoConfigsOverride };
this.cloudConnection = createCloudConnectionLibrary(this.logtoConfigs); this.cloudConnection = createCloudConnectionLibrary(this.logtoConfigs);
this.connectors = { this.connectors = {