mirror of
https://github.com/logto-io/logto.git
synced 2025-03-17 22:31:28 -05:00
test(core): add ut for queires (#287)
* test(core): add ut for queires add ut for queries * test(core): add user query ut add user query ut * fix(core): remove test code remove console log
This commit is contained in:
parent
c9400d0a4d
commit
fb6a1dc236
21 changed files with 1469 additions and 34 deletions
|
@ -3,7 +3,7 @@ import snakecaseKeys from 'snakecase-keys';
|
|||
|
||||
import {
|
||||
consumeInstanceById,
|
||||
destoryInstanceById,
|
||||
destroyInstanceById,
|
||||
findPayloadById,
|
||||
findPayloadByPayloadField,
|
||||
revokeInstanceByGrantId,
|
||||
|
@ -23,7 +23,7 @@ jest.mock('@/queries/oidc-model-instance', () => ({
|
|||
findPayloadById: jest.fn(),
|
||||
findPayloadByPayloadField: jest.fn(),
|
||||
consumeInstanceById: jest.fn(),
|
||||
destoryInstanceById: jest.fn(),
|
||||
destroyInstanceById: jest.fn(),
|
||||
revokeInstanceByGrantId: jest.fn(),
|
||||
}));
|
||||
|
||||
|
@ -102,7 +102,7 @@ describe('postgres Adapter', () => {
|
|||
expect(consumeInstanceById).toBeCalledWith(modelName, id);
|
||||
|
||||
await adapter.destroy(id);
|
||||
expect(destoryInstanceById).toBeCalledWith(modelName, id);
|
||||
expect(destroyInstanceById).toBeCalledWith(modelName, id);
|
||||
|
||||
await adapter.revokeByGrantId(grantId);
|
||||
expect(revokeInstanceByGrantId).toBeCalledWith(modelName, grantId);
|
||||
|
|
|
@ -6,7 +6,7 @@ import snakecaseKeys from 'snakecase-keys';
|
|||
import { findApplicationById } from '@/queries/application';
|
||||
import {
|
||||
consumeInstanceById,
|
||||
destoryInstanceById,
|
||||
destroyInstanceById,
|
||||
findPayloadById,
|
||||
findPayloadByPayloadField,
|
||||
revokeInstanceByGrantId,
|
||||
|
@ -57,7 +57,7 @@ export default function postgresAdapter(modelName: string): ReturnType<AdapterFa
|
|||
findByUserCode: async (userCode) => findPayloadByPayloadField(modelName, 'userCode', userCode),
|
||||
findByUid: async (uid) => findPayloadByPayloadField(modelName, 'uid', uid),
|
||||
consume: async (id) => consumeInstanceById(modelName, id),
|
||||
destroy: async (id) => destoryInstanceById(modelName, id),
|
||||
destroy: async (id) => destroyInstanceById(modelName, id),
|
||||
revokeByGrantId: async (grantId) => revokeInstanceByGrantId(modelName, grantId),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
import { Applications } from '@logto/schemas';
|
||||
import {
|
||||
createMockPool,
|
||||
createMockQueryResult,
|
||||
sql,
|
||||
QueryResultType,
|
||||
QueryResultRowType,
|
||||
} from 'slonik';
|
||||
import { PrimitiveValueExpressionType } from 'slonik/dist/src/types.d';
|
||||
import { createMockPool, createMockQueryResult, sql } from 'slonik';
|
||||
import { snakeCase } from 'snake-case';
|
||||
|
||||
import {
|
||||
|
@ -16,7 +9,7 @@ import {
|
|||
} from '@/database/utils';
|
||||
import { DeletionError } from '@/errors/SlonikError';
|
||||
import { mockApplication } from '@/utils/mock';
|
||||
import { expectSqlAssert } from '@/utils/test-utils';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
import {
|
||||
findTotalNumberOfApplications,
|
||||
|
@ -27,12 +20,7 @@ import {
|
|||
deleteApplicationById,
|
||||
} from './application';
|
||||
|
||||
type MockQuery = (
|
||||
sql: string,
|
||||
values: PrimitiveValueExpressionType
|
||||
) => Promise<QueryResultType<QueryResultRowType>>;
|
||||
|
||||
const mockQuery: jest.MockedFunction<MockQuery> = jest.fn();
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
createMockPool({
|
||||
|
@ -42,7 +30,7 @@ jest.mock('@/database/pool', () =>
|
|||
})
|
||||
);
|
||||
|
||||
describe('appliaction query', () => {
|
||||
describe('application query', () => {
|
||||
const { table, fields } = convertToIdentifiers(Applications);
|
||||
|
||||
it('findTotalNumberOfApplications', async () => {
|
||||
|
|
132
packages/core/src/queries/connector.test.ts
Normal file
132
packages/core/src/queries/connector.test.ts
Normal file
|
@ -0,0 +1,132 @@
|
|||
import { Connectors, CreateConnector } from '@logto/schemas';
|
||||
import { createMockPool, createMockQueryResult, sql, QueryResultRowType } from 'slonik';
|
||||
|
||||
import { convertToIdentifiers } from '@/database/utils';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
import {
|
||||
findAllConnectors,
|
||||
findConnectorById,
|
||||
hasConnector,
|
||||
insertConnector,
|
||||
updateConnector,
|
||||
} from './connector';
|
||||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
describe('connector queries', () => {
|
||||
const { table, fields } = convertToIdentifiers(Connectors);
|
||||
|
||||
it('findAllConnectors', async () => {
|
||||
const rowData = { id: 'foo' };
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([]);
|
||||
|
||||
return createMockQueryResult([rowData]);
|
||||
});
|
||||
|
||||
await expect(findAllConnectors()).resolves.toEqual([rowData]);
|
||||
});
|
||||
|
||||
it('findConnectorById', async () => {
|
||||
const id = 'foo';
|
||||
const rowData = { id };
|
||||
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.id}=$1
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([id]);
|
||||
|
||||
return createMockQueryResult([rowData]);
|
||||
});
|
||||
|
||||
await expect(findConnectorById(id)).resolves.toEqual(rowData);
|
||||
});
|
||||
|
||||
it('hasConnector', async () => {
|
||||
const id = 'foo';
|
||||
|
||||
const expectSql = sql`
|
||||
SELECT EXISTS(
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.id}=$1
|
||||
)
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([id]);
|
||||
|
||||
return createMockQueryResult([{ exists: true }]);
|
||||
});
|
||||
|
||||
await expect(hasConnector(id)).resolves.toEqual(true);
|
||||
});
|
||||
|
||||
it('insertConnector', async () => {
|
||||
const connector: CreateConnector & QueryResultRowType = {
|
||||
id: 'foo',
|
||||
enabled: true,
|
||||
};
|
||||
|
||||
const expectSql = `
|
||||
insert into "connectors" ("id", "enabled")
|
||||
values ($1, $2)
|
||||
returning *
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql);
|
||||
|
||||
expect(values).toEqual([connector.id, connector.enabled]);
|
||||
|
||||
return createMockQueryResult([connector]);
|
||||
});
|
||||
|
||||
await expect(insertConnector(connector)).resolves.toEqual(connector);
|
||||
});
|
||||
|
||||
it('updateConnector', async () => {
|
||||
const id = 'foo';
|
||||
const enabled = false;
|
||||
|
||||
const expectSql = sql`
|
||||
update ${table}
|
||||
set ${fields.enabled}=$1
|
||||
where ${fields.id}=$2
|
||||
returning *
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([enabled, id]);
|
||||
|
||||
return createMockQueryResult([{ id, enabled }]);
|
||||
});
|
||||
|
||||
await expect(updateConnector({ where: { id }, set: { enabled } })).resolves.toEqual({
|
||||
id,
|
||||
enabled,
|
||||
});
|
||||
});
|
||||
});
|
166
packages/core/src/queries/oidc-model-instance.test.ts
Normal file
166
packages/core/src/queries/oidc-model-instance.test.ts
Normal file
|
@ -0,0 +1,166 @@
|
|||
import { OidcModelInstances, CreateOidcModelInstance } from '@logto/schemas';
|
||||
import { createMockPool, createMockQueryResult, sql } from 'slonik';
|
||||
|
||||
import { convertToIdentifiers } from '@/database/utils';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
import {
|
||||
upsertInstance,
|
||||
findPayloadById,
|
||||
findPayloadByPayloadField,
|
||||
consumeInstanceById,
|
||||
destroyInstanceById,
|
||||
revokeInstanceByGrantId,
|
||||
} from './oidc-model-instance';
|
||||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
jest.mock('@/database/utils', () => ({
|
||||
...jest.requireActual('@/database/utils'),
|
||||
convertToTimestamp: () => 100,
|
||||
}));
|
||||
|
||||
describe('oidc-model-instance query', () => {
|
||||
const { table, fields } = convertToIdentifiers(OidcModelInstances);
|
||||
const expiresAt = Date.now();
|
||||
const instance: CreateOidcModelInstance = {
|
||||
modelName: 'access_token',
|
||||
id: 'foo',
|
||||
payload: {},
|
||||
expiresAt,
|
||||
};
|
||||
const databaseValue = {
|
||||
...instance,
|
||||
payload: JSON.stringify(instance.payload),
|
||||
};
|
||||
|
||||
it('upsertInstance', async () => {
|
||||
const expectSql = sql`
|
||||
insert into ${table} ("model_name", "id", "payload", "expires_at")
|
||||
values ($1, $2, $3, to_timestamp($4))
|
||||
on conflict ("model_name", "id") do update
|
||||
set "payload"=excluded."payload", "expires_at"=excluded."expires_at"
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([
|
||||
instance.modelName,
|
||||
instance.id,
|
||||
JSON.stringify(instance.payload),
|
||||
instance.expiresAt / 1000,
|
||||
]);
|
||||
|
||||
return createMockQueryResult([databaseValue]);
|
||||
});
|
||||
|
||||
await expect(upsertInstance(instance)).resolves.toEqual(databaseValue);
|
||||
});
|
||||
|
||||
it('findPayloadById', async () => {
|
||||
const expectSql = sql`
|
||||
select ${fields.payload}, ${fields.consumedAt}
|
||||
from ${table}
|
||||
where "model_name"=$1
|
||||
and "id"=$2
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([instance.modelName, instance.id]);
|
||||
|
||||
return createMockQueryResult([{ consumedAt: 10 }]);
|
||||
});
|
||||
|
||||
await expect(findPayloadById(instance.modelName, instance.id)).resolves.toEqual({
|
||||
consumed: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('findPayloadByPayloadField', async () => {
|
||||
const uid_key = 'uid';
|
||||
const uid_value = 'foo';
|
||||
|
||||
const expectSql = sql`
|
||||
select ${fields.payload}, ${fields.consumedAt}
|
||||
from ${table}
|
||||
where ${fields.modelName}=$1
|
||||
and ${fields.payload}->>$2=$3
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([instance.modelName, uid_key, uid_value]);
|
||||
|
||||
return createMockQueryResult([{ consumedAt: 10 }]);
|
||||
});
|
||||
|
||||
await expect(
|
||||
findPayloadByPayloadField(instance.modelName, uid_key, uid_value)
|
||||
).resolves.toEqual({
|
||||
consumed: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('consumeInstanceById', async () => {
|
||||
const expectSql = sql`
|
||||
update ${table}
|
||||
set ${fields.consumedAt}=$1
|
||||
where ${fields.modelName}=$2
|
||||
and ${fields.id}=$3
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([100, instance.modelName, instance.id]);
|
||||
|
||||
return createMockQueryResult([]);
|
||||
});
|
||||
|
||||
await consumeInstanceById(instance.modelName, instance.id);
|
||||
});
|
||||
|
||||
it('destroyInstanceById', async () => {
|
||||
const expectSql = sql`
|
||||
delete from ${table}
|
||||
where ${fields.modelName}=$1
|
||||
and ${fields.id}=$2
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([instance.modelName, instance.id]);
|
||||
|
||||
return createMockQueryResult([]);
|
||||
});
|
||||
|
||||
await destroyInstanceById(instance.modelName, instance.id);
|
||||
});
|
||||
|
||||
it('revokeInstanceByGrantId', async () => {
|
||||
const grantId = 'grant';
|
||||
|
||||
const expectSql = sql`
|
||||
delete from ${table}
|
||||
where ${fields.modelName}=$1
|
||||
and ${fields.payload}->>'grantId'=$2
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([instance.modelName, grantId]);
|
||||
|
||||
return createMockQueryResult([]);
|
||||
});
|
||||
|
||||
await revokeInstanceByGrantId(instance.modelName, grantId);
|
||||
});
|
||||
});
|
|
@ -71,7 +71,7 @@ export const consumeInstanceById = async (modelName: string, id: string) => {
|
|||
`);
|
||||
};
|
||||
|
||||
export const destoryInstanceById = async (modelName: string, id: string) => {
|
||||
export const destroyInstanceById = async (modelName: string, id: string) => {
|
||||
await pool.query(sql`
|
||||
delete from ${table}
|
||||
where ${fields.modelName}=${modelName}
|
||||
|
|
191
packages/core/src/queries/passcode.test.ts
Normal file
191
packages/core/src/queries/passcode.test.ts
Normal file
|
@ -0,0 +1,191 @@
|
|||
import { Passcodes, PasscodeType } from '@logto/schemas';
|
||||
import { createMockPool, createMockQueryResult, sql } from 'slonik';
|
||||
import { snakeCase } from 'snake-case';
|
||||
|
||||
import {
|
||||
convertToIdentifiers,
|
||||
convertToPrimitiveOrSql,
|
||||
excludeAutoSetFields,
|
||||
} from '@/database/utils';
|
||||
import { DeletionError } from '@/errors/SlonikError';
|
||||
import { mockPasscode } from '@/utils/mock';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
import {
|
||||
findUnconsumedPasscodeByJtiAndType,
|
||||
findUnconsumedPasscodesByJtiAndType,
|
||||
insertPasscode,
|
||||
updatePasscode,
|
||||
deletePasscodeById,
|
||||
deletePasscodesByIds,
|
||||
} from './passcode';
|
||||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
describe('passcode query', () => {
|
||||
const { table, fields } = convertToIdentifiers(Passcodes);
|
||||
|
||||
it('findUnconsumedPasscodeByJtiAndType', async () => {
|
||||
const jti = 'foo';
|
||||
const type = PasscodeType.SignIn;
|
||||
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.interactionJti}=$1 and ${fields.type}=$2 and ${fields.consumed} = false
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([jti, type]);
|
||||
|
||||
return createMockQueryResult([mockPasscode]);
|
||||
});
|
||||
|
||||
await expect(findUnconsumedPasscodeByJtiAndType(jti, type)).resolves.toEqual(mockPasscode);
|
||||
});
|
||||
|
||||
it('findUnconsumedPasscodesByJtiAndType', async () => {
|
||||
const jti = 'foo';
|
||||
const type = PasscodeType.SignIn;
|
||||
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.interactionJti}=$1 and ${fields.type}=$2 and ${fields.consumed} = false
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([jti, type]);
|
||||
|
||||
return createMockQueryResult([mockPasscode]);
|
||||
});
|
||||
|
||||
await expect(findUnconsumedPasscodesByJtiAndType(jti, type)).resolves.toEqual([mockPasscode]);
|
||||
});
|
||||
|
||||
it('insertPasscode', async () => {
|
||||
const keys = excludeAutoSetFields(Passcodes.fieldKeys);
|
||||
|
||||
const expectSql = `
|
||||
insert into "passcodes" (${keys.map((k) => `"${snakeCase(k)}"`).join(', ')})
|
||||
values (${keys.map((_, index) => `$${index + 1}`).join(', ')})
|
||||
returning *
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql);
|
||||
|
||||
expect(values).toEqual(keys.map((k) => convertToPrimitiveOrSql(k, mockPasscode[k])));
|
||||
|
||||
return createMockQueryResult([mockPasscode]);
|
||||
});
|
||||
|
||||
await expect(insertPasscode(mockPasscode)).resolves.toEqual(mockPasscode);
|
||||
});
|
||||
|
||||
it('updatePasscode', async () => {
|
||||
const id = 'foo';
|
||||
const tryCount = 3;
|
||||
|
||||
const expectSql = sql`
|
||||
update ${table}
|
||||
set ${fields.tryCount}=$1
|
||||
where ${fields.id}=$2
|
||||
returning *
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([tryCount, id]);
|
||||
|
||||
return createMockQueryResult([{ ...mockPasscode, tryCount }]);
|
||||
});
|
||||
|
||||
await expect(updatePasscode({ where: { id }, set: { tryCount } })).resolves.toEqual({
|
||||
...mockPasscode,
|
||||
tryCount,
|
||||
});
|
||||
});
|
||||
|
||||
it('deletePasscodeById', async () => {
|
||||
const id = 'foo';
|
||||
const expectSql = sql`
|
||||
delete from ${table}
|
||||
where ${fields.id}=$1
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([id]);
|
||||
|
||||
return createMockQueryResult([mockPasscode]);
|
||||
});
|
||||
|
||||
await deletePasscodeById(id);
|
||||
});
|
||||
|
||||
it('deletePasscodeById throw error if return row count is 0', async () => {
|
||||
const id = 'foo';
|
||||
const expectSql = sql`
|
||||
delete from ${table}
|
||||
where ${fields.id}=$1
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([id]);
|
||||
|
||||
return createMockQueryResult([]);
|
||||
});
|
||||
|
||||
await expect(deletePasscodeById(id)).rejects.toMatchError(
|
||||
new DeletionError(Passcodes.table, id)
|
||||
);
|
||||
});
|
||||
|
||||
it('deletePasscodesByIds', async () => {
|
||||
const ids = ['foo', 'foo2'];
|
||||
const expectSql = sql`
|
||||
delete from ${table}
|
||||
where ${fields.id} in (${ids.join(',')})
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([ids.join(',')]);
|
||||
|
||||
return createMockQueryResult([mockPasscode, mockPasscode]);
|
||||
});
|
||||
|
||||
await deletePasscodesByIds(ids);
|
||||
});
|
||||
|
||||
it('deletePasscodesByIds throw error if return row count not match requested id length', async () => {
|
||||
const ids = ['foo', 'foo2'];
|
||||
const expectSql = sql`
|
||||
delete from ${table}
|
||||
where ${fields.id} in (${ids.join(',')})
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([ids.join(',')]);
|
||||
|
||||
return createMockQueryResult([mockPasscode]);
|
||||
});
|
||||
|
||||
await expect(deletePasscodesByIds(ids)).rejects.toMatchError(
|
||||
new DeletionError(Passcodes.table, `${ids.join(',')}`)
|
||||
);
|
||||
});
|
||||
});
|
|
@ -32,7 +32,7 @@ export const updatePasscode = buildUpdateWhere<CreatePasscode, Passcode>(pool, P
|
|||
export const deletePasscodeById = async (id: string) => {
|
||||
const { rowCount } = await pool.query(sql`
|
||||
delete from ${table}
|
||||
where id=${id}
|
||||
where ${fields.id}=${id}
|
||||
`);
|
||||
|
||||
if (rowCount < 1) {
|
||||
|
@ -43,7 +43,7 @@ export const deletePasscodeById = async (id: string) => {
|
|||
export const deletePasscodesByIds = async (ids: string[]) => {
|
||||
const { rowCount } = await pool.query(sql`
|
||||
delete from ${table}
|
||||
where id in (${ids.join(',')})
|
||||
where ${fields.id} in (${ids.join(',')})
|
||||
`);
|
||||
|
||||
if (rowCount !== ids.length) {
|
||||
|
|
179
packages/core/src/queries/resource.test.ts
Normal file
179
packages/core/src/queries/resource.test.ts
Normal file
|
@ -0,0 +1,179 @@
|
|||
import { Resources } from '@logto/schemas';
|
||||
import { createMockPool, createMockQueryResult, sql } from 'slonik';
|
||||
|
||||
import { convertToIdentifiers, convertToPrimitiveOrSql } from '@/database/utils';
|
||||
import { DeletionError } from '@/errors/SlonikError';
|
||||
import { mockResource } from '@/utils/mock';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
import {
|
||||
findTotalNumberOfResources,
|
||||
findAllResources,
|
||||
findResourceById,
|
||||
findResourceByIndicator,
|
||||
insertResource,
|
||||
updateResourceById,
|
||||
deleteResourceById,
|
||||
} from './resource';
|
||||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
describe('resource query', () => {
|
||||
const { table, fields } = convertToIdentifiers(Resources);
|
||||
|
||||
it('findTotalNumberOfResources', async () => {
|
||||
const expectSql = sql`
|
||||
select count(*)
|
||||
from ${table}
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual(expectSql.values);
|
||||
|
||||
return createMockQueryResult([{ count: 10 }]);
|
||||
});
|
||||
|
||||
await expect(findTotalNumberOfResources()).resolves.toEqual({ count: 10 });
|
||||
});
|
||||
|
||||
it('findAllResources', async () => {
|
||||
const limit = 10;
|
||||
const offset = 1;
|
||||
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
limit $1
|
||||
offset $2
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([limit, offset]);
|
||||
|
||||
return createMockQueryResult([mockResource]);
|
||||
});
|
||||
|
||||
await expect(findAllResources(limit, offset)).resolves.toEqual([mockResource]);
|
||||
});
|
||||
|
||||
it('findResourcesById', async () => {
|
||||
const id = 'foo';
|
||||
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.id}=$1
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([id]);
|
||||
|
||||
return createMockQueryResult([mockResource]);
|
||||
});
|
||||
|
||||
await expect(findResourceById(id)).resolves.toEqual(mockResource);
|
||||
});
|
||||
|
||||
it('findResourceByIndicator', async () => {
|
||||
const indicator = 'foo';
|
||||
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.indicator}=$1
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([indicator]);
|
||||
|
||||
return createMockQueryResult([mockResource]);
|
||||
});
|
||||
|
||||
await expect(findResourceByIndicator(indicator)).resolves.toEqual(mockResource);
|
||||
});
|
||||
|
||||
it('insertResource', async () => {
|
||||
const expectSql = sql`
|
||||
insert into ${table} (${sql.join(Object.values(fields), sql`, `)})
|
||||
values (${sql.join(
|
||||
Object.values(fields).map((_, index) => `$${index + 1}`),
|
||||
sql`, `
|
||||
)})
|
||||
returning *
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
|
||||
expect(values).toEqual(
|
||||
Resources.fieldKeys.map((k) => convertToPrimitiveOrSql(k, mockResource[k]))
|
||||
);
|
||||
|
||||
return createMockQueryResult([mockResource]);
|
||||
});
|
||||
|
||||
await expect(insertResource(mockResource)).resolves.toEqual(mockResource);
|
||||
});
|
||||
|
||||
it('updateResourceById', async () => {
|
||||
const id = 'foo';
|
||||
const name = 'foo';
|
||||
|
||||
const expectSql = sql`
|
||||
update ${table}
|
||||
set ${fields.name}=$1
|
||||
where ${fields.id}=$2
|
||||
returning *
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([name, id]);
|
||||
|
||||
return createMockQueryResult([mockResource]);
|
||||
});
|
||||
|
||||
await expect(updateResourceById(id, { name })).resolves.toEqual(mockResource);
|
||||
});
|
||||
|
||||
it('deleteResourceById', async () => {
|
||||
const id = 'foo';
|
||||
const expectSql = sql`
|
||||
delete from ${table}
|
||||
where ${fields.id}=$1
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([id]);
|
||||
|
||||
return createMockQueryResult([mockResource]);
|
||||
});
|
||||
|
||||
await deleteResourceById(id);
|
||||
});
|
||||
|
||||
it('deleteResourceById throw error if return row count is 0', async () => {
|
||||
const id = 'foo';
|
||||
|
||||
mockQuery.mockImplementationOnce(async () => {
|
||||
return createMockQueryResult([]);
|
||||
});
|
||||
|
||||
await expect(deleteResourceById(id)).rejects.toMatchError(
|
||||
new DeletionError(Resources.table, id)
|
||||
);
|
||||
});
|
||||
});
|
|
@ -19,14 +19,14 @@ export const findAllResources = async (limit: number, offset: number) =>
|
|||
|
||||
export const findResourceByIndicator = async (indicator: string) =>
|
||||
pool.maybeOne<Resource>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.indicator}=${indicator}
|
||||
`);
|
||||
|
||||
export const findResourceById = async (id: string) =>
|
||||
pool.one<Resource>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.id}=${id}
|
||||
`);
|
||||
|
@ -45,7 +45,7 @@ export const updateResourceById = async (
|
|||
export const deleteResourceById = async (id: string) => {
|
||||
const { rowCount } = await pool.query(sql`
|
||||
delete from ${table}
|
||||
where id=${id}
|
||||
where ${fields.id}=${id}
|
||||
`);
|
||||
|
||||
if (rowCount < 1) {
|
||||
|
|
57
packages/core/src/queries/roles.test.ts
Normal file
57
packages/core/src/queries/roles.test.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
import { Roles } from '@logto/schemas';
|
||||
import { createMockPool, createMockQueryResult, sql } from 'slonik';
|
||||
|
||||
import { convertToIdentifiers } from '@/database/utils';
|
||||
import { mockRole } from '@/utils/mock';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
import { findAllRoles, findRolesByRoleNames } from './roles';
|
||||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
describe('roles query', () => {
|
||||
const { table, fields } = convertToIdentifiers(Roles);
|
||||
|
||||
it('findAllRoles', async () => {
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([]);
|
||||
|
||||
return createMockQueryResult([mockRole]);
|
||||
});
|
||||
|
||||
await expect(findAllRoles()).resolves.toEqual([mockRole]);
|
||||
});
|
||||
|
||||
it('findRolesByRoleNames', async () => {
|
||||
const roleNames = ['foo'];
|
||||
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.name} in (${sql.join(roleNames, sql`, `)})
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([roleNames.join(', ')]);
|
||||
|
||||
return createMockQueryResult([mockRole]);
|
||||
});
|
||||
|
||||
await expect(findRolesByRoleNames(roleNames)).resolves.toEqual([mockRole]);
|
||||
});
|
||||
});
|
|
@ -14,7 +14,7 @@ export const findAllRoles = async () =>
|
|||
|
||||
export const findRolesByRoleNames = async (roleNames: string[]) =>
|
||||
pool.any<Role>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.name} in (${sql.join(roleNames, sql`,`)})
|
||||
where ${fields.name} in (${sql.join(roleNames, sql`, `)})
|
||||
`);
|
||||
|
|
99
packages/core/src/queries/scope.test.ts
Normal file
99
packages/core/src/queries/scope.test.ts
Normal file
|
@ -0,0 +1,99 @@
|
|||
import { ResourceScopes } from '@logto/schemas';
|
||||
import { createMockPool, createMockQueryResult, sql } from 'slonik';
|
||||
|
||||
import { convertToIdentifiers, convertToPrimitiveOrSql } from '@/database/utils';
|
||||
import { DeletionError } from '@/errors/SlonikError';
|
||||
import { mockScope } from '@/utils/mock';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
import { findAllScopesWithResourceId, insertScope, deleteScopeById } from './scope';
|
||||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
describe('scope query', () => {
|
||||
const { table, fields } = convertToIdentifiers(ResourceScopes);
|
||||
|
||||
it('findAllScopesWithResourceId', async () => {
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.resourceId}=$1
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([mockScope.resourceId]);
|
||||
|
||||
return createMockQueryResult([mockScope]);
|
||||
});
|
||||
|
||||
await expect(findAllScopesWithResourceId(mockScope.resourceId)).resolves.toEqual([mockScope]);
|
||||
});
|
||||
|
||||
it('insertScope', async () => {
|
||||
const expectSql = sql`
|
||||
insert into ${table} (${sql.join(Object.values(fields), sql`, `)})
|
||||
values (${sql.join(
|
||||
Object.values(fields).map((_, index) => `$${index + 1}`),
|
||||
sql`, `
|
||||
)})
|
||||
returning *
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
|
||||
expect(values).toEqual(
|
||||
ResourceScopes.fieldKeys.map((k) => convertToPrimitiveOrSql(k, mockScope[k]))
|
||||
);
|
||||
|
||||
return createMockQueryResult([mockScope]);
|
||||
});
|
||||
|
||||
await expect(insertScope(mockScope)).resolves.toEqual(mockScope);
|
||||
});
|
||||
|
||||
it('deleteScopeById', async () => {
|
||||
const id = 'foo';
|
||||
const expectSql = sql`
|
||||
delete from ${table}
|
||||
where ${fields.id}=$1
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([id]);
|
||||
|
||||
return createMockQueryResult([mockScope]);
|
||||
});
|
||||
|
||||
await deleteScopeById(id);
|
||||
});
|
||||
|
||||
it('deleteScopeById throw error if return row count is 0', async () => {
|
||||
const id = 'foo';
|
||||
const expectSql = sql`
|
||||
delete from ${table}
|
||||
where ${fields.id}=$1
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([id]);
|
||||
|
||||
return createMockQueryResult([]);
|
||||
});
|
||||
|
||||
await expect(deleteScopeById(id)).rejects.toMatchError(
|
||||
new DeletionError(ResourceScopes.table, id)
|
||||
);
|
||||
});
|
||||
});
|
|
@ -10,7 +10,7 @@ const { table, fields } = convertToIdentifiers(ResourceScopes);
|
|||
|
||||
export const findAllScopesWithResourceId = async (resourceId: string) =>
|
||||
pool.any<ResourceScope>(sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.resourceId}=${resourceId}
|
||||
`);
|
||||
|
@ -26,7 +26,7 @@ export const insertScope = buildInsertInto<CreateResourceScope, ResourceScope>(
|
|||
export const deleteScopeById = async (id: string) => {
|
||||
const { rowCount } = await pool.query(sql`
|
||||
delete from ${table}
|
||||
where id=${id}
|
||||
where ${fields.id}=${id}
|
||||
`);
|
||||
|
||||
if (rowCount < 1) {
|
||||
|
|
60
packages/core/src/queries/setting.test.ts
Normal file
60
packages/core/src/queries/setting.test.ts
Normal file
|
@ -0,0 +1,60 @@
|
|||
import { Settings } from '@logto/schemas';
|
||||
import { createMockPool, createMockQueryResult, sql } from 'slonik';
|
||||
|
||||
import { convertToIdentifiers } from '@/database/utils';
|
||||
import { mockSetting } from '@/utils/mock';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
import { defaultSettingId, getSetting, updateSetting } from './setting';
|
||||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
describe('setting query', () => {
|
||||
const { table, fields } = convertToIdentifiers(Settings);
|
||||
const dbvalue = { ...mockSetting, adminConsole: JSON.stringify(mockSetting.adminConsole) };
|
||||
|
||||
it('getSetting', async () => {
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
where ${fields.id}=$1
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([defaultSettingId]);
|
||||
|
||||
return createMockQueryResult([dbvalue]);
|
||||
});
|
||||
|
||||
await expect(getSetting()).resolves.toEqual(dbvalue);
|
||||
});
|
||||
|
||||
it('updateSetting', async () => {
|
||||
const customDomain = 'logto.io';
|
||||
|
||||
const expectSql = sql`
|
||||
update ${table}
|
||||
set ${fields.customDomain}=$1
|
||||
where ${fields.id}=$2
|
||||
returning *
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([customDomain, defaultSettingId]);
|
||||
|
||||
return createMockQueryResult([dbvalue]);
|
||||
});
|
||||
|
||||
await expect(updateSetting({ customDomain })).resolves.toEqual(dbvalue);
|
||||
});
|
||||
});
|
71
packages/core/src/queries/sign-in-experience.test.ts
Normal file
71
packages/core/src/queries/sign-in-experience.test.ts
Normal file
|
@ -0,0 +1,71 @@
|
|||
import { SignInExperiences } from '@logto/schemas';
|
||||
import { createMockPool, createMockQueryResult, sql } from 'slonik';
|
||||
|
||||
import { convertToIdentifiers } from '@/database/utils';
|
||||
import { mockSignInExperience } from '@/utils/mock';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
import { findDefaultSignInExperience, updateSignInExperienceById } from './sign-in-experience';
|
||||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
describe('sign-in-experience query', () => {
|
||||
const { table, fields } = convertToIdentifiers(SignInExperiences);
|
||||
const dbvalue = {
|
||||
...mockSignInExperience,
|
||||
companyInfo: JSON.stringify(mockSignInExperience.companyInfo),
|
||||
branding: JSON.stringify(mockSignInExperience.branding),
|
||||
termsOfUse: JSON.stringify(mockSignInExperience.termsOfUse),
|
||||
localization: JSON.stringify(mockSignInExperience.localization),
|
||||
signInMethods: JSON.stringify(mockSignInExperience.signInMethods),
|
||||
};
|
||||
|
||||
it('findDefaultSignInExperience', async () => {
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`, `)}
|
||||
from ${table}
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([]);
|
||||
|
||||
return createMockQueryResult([dbvalue]);
|
||||
});
|
||||
|
||||
await expect(findDefaultSignInExperience()).resolves.toEqual(dbvalue);
|
||||
});
|
||||
|
||||
it('updateSignInExperienceById', async () => {
|
||||
const id = 'foo';
|
||||
const termsOfUse = {
|
||||
enabled: false,
|
||||
};
|
||||
|
||||
const expectSql = sql`
|
||||
update ${table}
|
||||
set
|
||||
${fields.termsOfUse}=
|
||||
coalesce(${fields.termsOfUse},'{}'::jsonb)|| $1
|
||||
where ${fields.id}=$2
|
||||
returning *
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([JSON.stringify(termsOfUse), id]);
|
||||
|
||||
return createMockQueryResult([dbvalue]);
|
||||
});
|
||||
|
||||
await expect(updateSignInExperienceById(id, { termsOfUse })).resolves.toEqual(dbvalue);
|
||||
});
|
||||
});
|
66
packages/core/src/queries/user-log.test.ts
Normal file
66
packages/core/src/queries/user-log.test.ts
Normal file
|
@ -0,0 +1,66 @@
|
|||
import { UserLogs } from '@logto/schemas';
|
||||
import { createMockPool, createMockQueryResult, sql } from 'slonik';
|
||||
import { snakeCase } from 'snake-case';
|
||||
|
||||
import {
|
||||
convertToIdentifiers,
|
||||
excludeAutoSetFields,
|
||||
convertToPrimitiveOrSql,
|
||||
} from '@/database/utils';
|
||||
import { mockUserLog } from '@/utils/mock';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
import { insertUserLog, findLogsByUserId } from './user-log';
|
||||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
describe('user-log query', () => {
|
||||
const { table, fields } = convertToIdentifiers(UserLogs);
|
||||
const dbvalue = { ...mockUserLog, payload: JSON.stringify(mockUserLog.payload) };
|
||||
|
||||
it('findLogsByUserId', async () => {
|
||||
const userId = 'foo';
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
from ${table}
|
||||
where ${fields.userId}=${userId}
|
||||
order by created_at desc
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([userId]);
|
||||
|
||||
return createMockQueryResult([dbvalue]);
|
||||
});
|
||||
|
||||
await expect(findLogsByUserId(userId)).resolves.toEqual([dbvalue]);
|
||||
});
|
||||
|
||||
it('insertUserLog', async () => {
|
||||
const keys = excludeAutoSetFields(UserLogs.fieldKeys);
|
||||
|
||||
// eslint-disable-next-line sql/no-unsafe-query
|
||||
const expectSql = `
|
||||
insert into "user_logs" (${keys.map((k) => `"${snakeCase(k)}"`).join(', ')})
|
||||
values (${keys.map((_, index) => `$${index + 1}`).join(', ')})
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql);
|
||||
expect(values).toEqual(keys.map((k) => convertToPrimitiveOrSql(k, mockUserLog[k])));
|
||||
|
||||
return createMockQueryResult([]);
|
||||
});
|
||||
|
||||
await insertUserLog(mockUserLog);
|
||||
});
|
||||
});
|
378
packages/core/src/queries/user.test.ts
Normal file
378
packages/core/src/queries/user.test.ts
Normal file
|
@ -0,0 +1,378 @@
|
|||
import { Users } from '@logto/schemas';
|
||||
import { createMockPool, createMockQueryResult, sql } from 'slonik';
|
||||
|
||||
import { convertToIdentifiers, convertToPrimitiveOrSql } from '@/database/utils';
|
||||
import { DeletionError } from '@/errors/SlonikError';
|
||||
import { mockUser } from '@/utils/mock';
|
||||
import { expectSqlAssert, QueryType } from '@/utils/test-utils';
|
||||
|
||||
import {
|
||||
findUserByUsername,
|
||||
findUserByEmail,
|
||||
findUserByPhone,
|
||||
findUserById,
|
||||
findUserByIdentity,
|
||||
hasUser,
|
||||
hasUserWithId,
|
||||
hasUserWithEmail,
|
||||
hasUserWithIdentity,
|
||||
hasUserWithPhone,
|
||||
insertUser,
|
||||
countUsers,
|
||||
findUsers,
|
||||
updateUserById,
|
||||
deleteUserById,
|
||||
clearUserCustomDataById,
|
||||
} from './user';
|
||||
|
||||
const mockQuery: jest.MockedFunction<QueryType> = jest.fn();
|
||||
|
||||
jest.mock('@/database/pool', () =>
|
||||
createMockPool({
|
||||
query: async (sql, values) => {
|
||||
return mockQuery(sql, values);
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
describe('user query', () => {
|
||||
const { table, fields } = convertToIdentifiers(Users);
|
||||
const dbvalue = {
|
||||
...mockUser,
|
||||
roleNames: JSON.stringify(mockUser.roleNames),
|
||||
identities: JSON.stringify(mockUser.identities),
|
||||
customData: JSON.stringify(mockUser.customData),
|
||||
};
|
||||
|
||||
it('findUserByUsername', async () => {
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
from ${table}
|
||||
where ${fields.username}=$1
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([mockUser.username]);
|
||||
|
||||
return createMockQueryResult([dbvalue]);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
await expect(findUserByUsername(mockUser.username!)).resolves.toEqual(dbvalue);
|
||||
});
|
||||
|
||||
it('findUserByEmail', async () => {
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
from ${table}
|
||||
where ${fields.primaryEmail}=$1
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([mockUser.primaryEmail]);
|
||||
|
||||
return createMockQueryResult([dbvalue]);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
await expect(findUserByEmail(mockUser.primaryEmail!)).resolves.toEqual(dbvalue);
|
||||
});
|
||||
|
||||
it('findUserByPhone', async () => {
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
from ${table}
|
||||
where ${fields.primaryPhone}=$1
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([mockUser.primaryPhone]);
|
||||
|
||||
return createMockQueryResult([dbvalue]);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
await expect(findUserByPhone(mockUser.primaryPhone!)).resolves.toEqual(dbvalue);
|
||||
});
|
||||
|
||||
it('findUserById', async () => {
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
from ${table}
|
||||
where ${fields.id}=$1
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([mockUser.id]);
|
||||
|
||||
return createMockQueryResult([dbvalue]);
|
||||
});
|
||||
|
||||
await expect(findUserById(mockUser.id)).resolves.toEqual(dbvalue);
|
||||
});
|
||||
|
||||
it('findUserByIdentity', async () => {
|
||||
const connectorId = 'github_foo';
|
||||
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
from ${table}
|
||||
where ${fields.identities}::json#>>'{${sql.identifier([connectorId])},userId}' = $1
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([mockUser.id]);
|
||||
|
||||
return createMockQueryResult([dbvalue]);
|
||||
});
|
||||
|
||||
await expect(findUserByIdentity(connectorId, mockUser.id)).resolves.toEqual(dbvalue);
|
||||
});
|
||||
|
||||
it('hasUser', async () => {
|
||||
const expectSql = sql`
|
||||
SELECT EXISTS(
|
||||
select ${fields.id}
|
||||
from ${table}
|
||||
where ${fields.username}=$1
|
||||
)
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([mockUser.username]);
|
||||
|
||||
return createMockQueryResult([{ exists: true }]);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
await expect(hasUser(mockUser.username!)).resolves.toEqual(true);
|
||||
});
|
||||
|
||||
it('hasUserWithId', async () => {
|
||||
const expectSql = sql`
|
||||
SELECT EXISTS(
|
||||
select ${fields.id}
|
||||
from ${table}
|
||||
where ${fields.id}=$1
|
||||
)
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([mockUser.id]);
|
||||
|
||||
return createMockQueryResult([{ exists: true }]);
|
||||
});
|
||||
|
||||
await expect(hasUserWithId(mockUser.id)).resolves.toEqual(true);
|
||||
});
|
||||
|
||||
it('hasUserWithEmail', async () => {
|
||||
const expectSql = sql`
|
||||
SELECT EXISTS(
|
||||
select ${fields.primaryEmail}
|
||||
from ${table}
|
||||
where ${fields.primaryEmail}=$1
|
||||
)
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([mockUser.primaryEmail]);
|
||||
|
||||
return createMockQueryResult([{ exists: true }]);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
await expect(hasUserWithEmail(mockUser.primaryEmail!)).resolves.toEqual(true);
|
||||
});
|
||||
|
||||
it('hasUserWithPhone', async () => {
|
||||
const expectSql = sql`
|
||||
SELECT EXISTS(
|
||||
select ${fields.primaryPhone}
|
||||
from ${table}
|
||||
where ${fields.primaryPhone}=$1
|
||||
)
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([mockUser.primaryPhone]);
|
||||
|
||||
return createMockQueryResult([{ exists: true }]);
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
await expect(hasUserWithPhone(mockUser.primaryPhone!)).resolves.toEqual(true);
|
||||
});
|
||||
|
||||
it('hasUserWithIdentity', async () => {
|
||||
const connectorId = 'github_foo';
|
||||
|
||||
const expectSql = sql`
|
||||
SELECT EXISTS(
|
||||
select ${fields.id}
|
||||
from ${table}
|
||||
where ${fields.identities}::json#>>'{${sql.identifier([connectorId])},userId}' = $1
|
||||
)
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([mockUser.id]);
|
||||
|
||||
return createMockQueryResult([{ exists: true }]);
|
||||
});
|
||||
|
||||
await expect(hasUserWithIdentity(connectorId, mockUser.id)).resolves.toEqual(true);
|
||||
});
|
||||
|
||||
it('insertUser', async () => {
|
||||
const expectSql = sql`
|
||||
insert into ${table} (${sql.join(Object.values(fields), sql`, `)})
|
||||
values (${sql.join(
|
||||
Object.values(fields).map((_, index) => `$${index + 1}`),
|
||||
sql`, `
|
||||
)})
|
||||
returning *
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
|
||||
expect(values).toEqual(Users.fieldKeys.map((k) => convertToPrimitiveOrSql(k, mockUser[k])));
|
||||
|
||||
return createMockQueryResult([dbvalue]);
|
||||
});
|
||||
|
||||
await expect(insertUser(mockUser)).resolves.toEqual(dbvalue);
|
||||
});
|
||||
|
||||
it('countUsers', async () => {
|
||||
const search = 'foo';
|
||||
const expectSql = sql`
|
||||
select count(*)
|
||||
from ${table}
|
||||
where ${fields.primaryEmail} like $1 or ${fields.primaryPhone} like $2 or ${fields.username} like $3 or ${fields.name} like $4
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([`%${search}%`, `%${search}%`, `%${search}%`, `%${search}%`]);
|
||||
|
||||
return createMockQueryResult([dbvalue]);
|
||||
});
|
||||
|
||||
await expect(countUsers(search)).resolves.toEqual(dbvalue);
|
||||
});
|
||||
|
||||
it('findUsers', async () => {
|
||||
const search = 'foo';
|
||||
const limit = 100;
|
||||
const offset = 1;
|
||||
const expectSql = sql`
|
||||
select ${sql.join(Object.values(fields), sql`,`)}
|
||||
from ${table}
|
||||
where ${fields.primaryEmail} like $1 or ${fields.primaryPhone} like $2 or ${
|
||||
fields.username
|
||||
} like $3 or ${fields.name} like $4
|
||||
limit $5
|
||||
offset $6
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([
|
||||
`%${search}%`,
|
||||
`%${search}%`,
|
||||
`%${search}%`,
|
||||
`%${search}%`,
|
||||
limit,
|
||||
offset,
|
||||
]);
|
||||
|
||||
return createMockQueryResult([dbvalue]);
|
||||
});
|
||||
|
||||
await expect(findUsers(limit, offset, search)).resolves.toEqual([dbvalue]);
|
||||
});
|
||||
|
||||
it('updateUserById', async () => {
|
||||
const username = 'Joe';
|
||||
const id = 'foo';
|
||||
const expectSql = sql`
|
||||
update ${table}
|
||||
set ${fields.username}=$1
|
||||
where ${fields.id}=$2
|
||||
returning *
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([username, id]);
|
||||
|
||||
return createMockQueryResult([dbvalue]);
|
||||
});
|
||||
|
||||
await expect(updateUserById(id, { username })).resolves.toEqual(dbvalue);
|
||||
});
|
||||
|
||||
it('deleteUserById', async () => {
|
||||
const id = 'foo';
|
||||
const expectSql = sql`
|
||||
delete from ${table}
|
||||
where ${fields.id}=$1
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([id]);
|
||||
|
||||
return createMockQueryResult([dbvalue]);
|
||||
});
|
||||
|
||||
await deleteUserById(id);
|
||||
});
|
||||
|
||||
it('deleteUserById should throw with zero response', async () => {
|
||||
const id = 'foo';
|
||||
const expectSql = sql`
|
||||
delete from ${table}
|
||||
where ${fields.id}=$1
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([id]);
|
||||
|
||||
return createMockQueryResult([]);
|
||||
});
|
||||
|
||||
await expect(deleteUserById(id)).rejects.toMatchError(new DeletionError(Users.table, id));
|
||||
});
|
||||
|
||||
it('clearUserCustomDataById', async () => {
|
||||
const id = 'foo';
|
||||
const expectSql = sql`
|
||||
update ${table}
|
||||
set ${fields.customData}='{}'::jsonb
|
||||
where ${fields.id}=$1
|
||||
`;
|
||||
|
||||
mockQuery.mockImplementationOnce(async (sql, values) => {
|
||||
expectSqlAssert(sql, expectSql.sql);
|
||||
expect(values).toEqual([id]);
|
||||
|
||||
return createMockQueryResult([dbvalue]);
|
||||
});
|
||||
|
||||
await clearUserCustomDataById(id);
|
||||
});
|
||||
});
|
|
@ -118,7 +118,7 @@ export const updateUserById = async (id: string, set: Partial<OmitAutoSetFields<
|
|||
export const deleteUserById = async (id: string) => {
|
||||
const { rowCount } = await pool.query(sql`
|
||||
delete from ${table}
|
||||
where id=${id}
|
||||
where ${fields.id}=${id}
|
||||
`);
|
||||
|
||||
if (rowCount < 1) {
|
||||
|
@ -130,7 +130,7 @@ export const clearUserCustomDataById = async (id: string) => {
|
|||
const { rowCount } = await pool.query<User>(sql`
|
||||
update ${table}
|
||||
set ${fields.customData}='{}'::jsonb
|
||||
where id=${id}
|
||||
where ${fields.id}=${id}
|
||||
`);
|
||||
|
||||
if (rowCount < 1) {
|
||||
|
|
|
@ -10,6 +10,12 @@ import {
|
|||
SignInExperience,
|
||||
BrandingStyle,
|
||||
Language,
|
||||
Connector,
|
||||
Passcode,
|
||||
PasscodeType,
|
||||
UserLog,
|
||||
UserLogType,
|
||||
UserLogResult,
|
||||
} from '@logto/schemas';
|
||||
import pick from 'lodash.pick';
|
||||
|
||||
|
@ -174,3 +180,31 @@ export const mockSignInExperience: SignInExperience = {
|
|||
disabled: [],
|
||||
},
|
||||
};
|
||||
|
||||
export const mockConnector: Connector = {
|
||||
id: 'foo',
|
||||
enabled: true,
|
||||
config: {},
|
||||
createdAt: 1_645_334_775_356,
|
||||
};
|
||||
|
||||
export const mockPasscode: Passcode = {
|
||||
id: 'foo',
|
||||
interactionJti: 'jti',
|
||||
phone: '888 888 8888',
|
||||
email: 'foo@logto.io',
|
||||
type: PasscodeType.SignIn,
|
||||
code: 'asdfghjkl',
|
||||
consumed: false,
|
||||
tryCount: 2,
|
||||
createdAt: 10,
|
||||
};
|
||||
|
||||
export const mockUserLog: UserLog = {
|
||||
id: 'foo',
|
||||
userId: 'foo',
|
||||
type: UserLogType.RegisterEmail,
|
||||
result: UserLogResult.Success,
|
||||
payload: {},
|
||||
createdAt: 10,
|
||||
};
|
||||
|
|
|
@ -2,12 +2,15 @@ import { createMockContext, Options } from '@shopify/jest-koa-mocks';
|
|||
import Koa, { MiddlewareType, Context, Middleware } from 'koa';
|
||||
import Router, { IRouterParamContext } from 'koa-router';
|
||||
import { Provider } from 'oidc-provider';
|
||||
import { createMockPool, createMockQueryResult, QueryResultRowType } from 'slonik';
|
||||
import { createMockPool, createMockQueryResult, QueryResultType, QueryResultRowType } from 'slonik';
|
||||
import { PrimitiveValueExpressionType } from 'slonik/dist/src/types.d';
|
||||
import request from 'supertest';
|
||||
|
||||
import { AuthedRouter, AnonymousRouter } from '@/routes/types';
|
||||
|
||||
/**
|
||||
* Slonik Query Mock Utils
|
||||
**/
|
||||
export const expectSqlAssert = (sql: string, expectSql: string) => {
|
||||
expect(
|
||||
sql
|
||||
|
@ -22,6 +25,11 @@ export const expectSqlAssert = (sql: string, expectSql: string) => {
|
|||
);
|
||||
};
|
||||
|
||||
export type QueryType = (
|
||||
sql: string,
|
||||
values: readonly PrimitiveValueExpressionType[]
|
||||
) => Promise<QueryResultType<QueryResultRowType>>;
|
||||
|
||||
export const createTestPool = <T extends QueryResultRowType>(
|
||||
expectSql?: string,
|
||||
returning?: T | ((sql: string, values: readonly PrimitiveValueExpressionType[]) => T)
|
||||
|
@ -38,6 +46,9 @@ export const createTestPool = <T extends QueryResultRowType>(
|
|||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* Middleware & Context Mock Utils
|
||||
**/
|
||||
export const emptyMiddleware =
|
||||
<StateT, ContextT>(): MiddlewareType<StateT, ContextT> =>
|
||||
// Intend to mock the async middleware
|
||||
|
@ -60,6 +71,9 @@ export const createContextWithRouteParameters = (
|
|||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Supertest Request Mock Utils
|
||||
**/
|
||||
type RouteLauncher<T extends AuthedRouter | AnonymousRouter> = (router: T) => void;
|
||||
|
||||
type ProviderRouteLauncher<T extends AuthedRouter | AnonymousRouter> = (
|
||||
|
|
Loading…
Add table
Reference in a new issue