Merge pull request #34267 from backstage/freben/describe-each-databases
tests: use describe.each for database test iteration
This commit is contained in:
+53
-56
@@ -24,13 +24,13 @@ import { ConfigReader } from '@backstage/config';
|
||||
|
||||
jest.setTimeout(60_000);
|
||||
|
||||
describe('createPluginKeySource', () => {
|
||||
const databases = TestDatabases.create();
|
||||
const mockDir = createMockDirectory();
|
||||
const databases = TestDatabases.create();
|
||||
const mockDir = createMockDirectory();
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'works for implicit database (no config), %p',
|
||||
async databaseId => {
|
||||
describe.each(databases.eachSupportedId())(
|
||||
'createPluginKeySource, %p',
|
||||
databaseId => {
|
||||
it('works for implicit database (no config)', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
|
||||
const getClient = jest.fn(async () => knex);
|
||||
@@ -61,12 +61,9 @@ describe('createPluginKeySource', () => {
|
||||
}),
|
||||
],
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'works for explicit database, %p',
|
||||
async databaseId => {
|
||||
it('works for explicit database', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
|
||||
const getClient = jest.fn(async () => knex);
|
||||
@@ -99,11 +96,12 @@ describe('createPluginKeySource', () => {
|
||||
}),
|
||||
],
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it('works for static', async () => {
|
||||
const privateKey = `
|
||||
it('works for static', async () => {
|
||||
const privateKey = `
|
||||
-----BEGIN PRIVATE KEY-----
|
||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgR8Ja2ppMEgOm1KeY
|
||||
Kpje00U1luybndt6yC263vcgeKqhRANCAAS+slUrS9JXgtHB1RcDnmlveuu4H3Zm
|
||||
@@ -111,60 +109,59 @@ describe('createPluginKeySource', () => {
|
||||
-----END PRIVATE KEY-----
|
||||
`.trim();
|
||||
|
||||
const publicKey = `
|
||||
const publicKey = `
|
||||
-----BEGIN PUBLIC KEY-----
|
||||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEvrJVK0vSV4LRwdUXA55pb3rruB92
|
||||
ZoUEY72HTvjIP9xSbLOhWhMREk84T0q91/m3v3sWq5EzHIWw1zRHQisKZw==
|
||||
-----END PUBLIC KEY-----
|
||||
`.trim();
|
||||
|
||||
mockDir.setContent({
|
||||
'public.pem': publicKey,
|
||||
'private.pem': privateKey,
|
||||
});
|
||||
const publicKeyPath = mockDir.resolve('public.pem');
|
||||
const privateKeyPath = mockDir.resolve('private.pem');
|
||||
mockDir.setContent({
|
||||
'public.pem': publicKey,
|
||||
'private.pem': privateKey,
|
||||
});
|
||||
const publicKeyPath = mockDir.resolve('public.pem');
|
||||
const privateKeyPath = mockDir.resolve('private.pem');
|
||||
|
||||
const getClient = jest.fn();
|
||||
const getClient = jest.fn();
|
||||
|
||||
const source = await createPluginKeySource({
|
||||
config: new ConfigReader({
|
||||
backend: {
|
||||
auth: {
|
||||
pluginKeyStore: {
|
||||
type: 'static',
|
||||
static: {
|
||||
keys: [
|
||||
{
|
||||
publicKeyFile: publicKeyPath,
|
||||
privateKeyFile: privateKeyPath,
|
||||
keyId: '1',
|
||||
},
|
||||
],
|
||||
},
|
||||
const source = await createPluginKeySource({
|
||||
config: new ConfigReader({
|
||||
backend: {
|
||||
auth: {
|
||||
pluginKeyStore: {
|
||||
type: 'static',
|
||||
static: {
|
||||
keys: [
|
||||
{
|
||||
publicKeyFile: publicKeyPath,
|
||||
privateKeyFile: privateKeyPath,
|
||||
keyId: '1',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
database: mockServices.database.mock({ getClient }),
|
||||
logger: mockServices.logger.mock(),
|
||||
keyDuration: { seconds: 10 },
|
||||
});
|
||||
},
|
||||
}),
|
||||
database: mockServices.database.mock({ getClient }),
|
||||
logger: mockServices.logger.mock(),
|
||||
keyDuration: { seconds: 10 },
|
||||
});
|
||||
|
||||
expect(getClient).not.toHaveBeenCalled();
|
||||
expect(getClient).not.toHaveBeenCalled();
|
||||
|
||||
const keys = await source.listKeys();
|
||||
expect(keys.keys.length).toEqual(1);
|
||||
expect(keys.keys[0].key).toMatchObject({
|
||||
kid: '1',
|
||||
alg: 'ES256',
|
||||
});
|
||||
const keys = await source.listKeys();
|
||||
expect(keys.keys.length).toEqual(1);
|
||||
expect(keys.keys[0].key).toMatchObject({
|
||||
kid: '1',
|
||||
alg: 'ES256',
|
||||
});
|
||||
|
||||
const pk = await source.getPrivateSigningKey();
|
||||
expect(pk).toMatchObject({
|
||||
kid: '1',
|
||||
alg: 'ES256',
|
||||
d: expect.any(String),
|
||||
});
|
||||
const pk = await source.getPrivateSigningKey();
|
||||
expect(pk).toMatchObject({
|
||||
kid: '1',
|
||||
alg: 'ES256',
|
||||
d: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
@@ -54,14 +54,16 @@ jest.mock('@keyv/memcache', () => {
|
||||
};
|
||||
});
|
||||
|
||||
describe('CacheManager integration', () => {
|
||||
const caches = TestCaches.create();
|
||||
jest.setTimeout(60_000);
|
||||
|
||||
afterEach(jest.clearAllMocks);
|
||||
const caches = TestCaches.create();
|
||||
|
||||
it.each(caches.eachSupportedId())(
|
||||
'only creates one underlying connection per plugin, %p',
|
||||
async cacheId => {
|
||||
describe.each(caches.eachSupportedId())(
|
||||
'CacheManager integration, %p',
|
||||
cacheId => {
|
||||
afterEach(jest.clearAllMocks);
|
||||
|
||||
it('only creates one underlying connection per plugin', async () => {
|
||||
const { store, connection } = await caches.init(cacheId);
|
||||
|
||||
const manager = CacheManager.fromConfig(
|
||||
@@ -85,12 +87,9 @@ describe('CacheManager integration', () => {
|
||||
// eslint-disable-next-line jest/no-conditional-expect
|
||||
expect(KeyvValkey).toHaveBeenCalledTimes(3);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it.each(caches.eachSupportedId())(
|
||||
'interacts correctly with store, %p',
|
||||
async cacheId => {
|
||||
it('interacts correctly with store', async () => {
|
||||
const { store, connection } = await caches.init(cacheId);
|
||||
|
||||
const manager = CacheManager.fromConfig(
|
||||
@@ -114,12 +113,9 @@ describe('CacheManager integration', () => {
|
||||
await expect(plugin1.get('a')).resolves.toBe('plugin1');
|
||||
await expect(plugin2a.get('a')).resolves.toBe('plugin2b');
|
||||
await expect(plugin2b.get('a')).resolves.toBe('plugin2b');
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it.each(caches.eachSupportedId())(
|
||||
'supports both milliseconds and human durations throughout, %p',
|
||||
async cacheId => {
|
||||
it('supports both milliseconds and human durations throughout', async () => {
|
||||
const { store, connection } = await caches.init(cacheId);
|
||||
|
||||
for (const defaultTtl of [200, { milliseconds: 200 }]) {
|
||||
@@ -175,39 +171,39 @@ describe('CacheManager integration', () => {
|
||||
await expect(defaultClient.get('e')).resolves.toBeUndefined();
|
||||
await expect(defaultClient.get('f')).resolves.toBeUndefined();
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it('rejects invalid defaultTtl', () => {
|
||||
expect(() =>
|
||||
CacheManager.fromConfig(
|
||||
mockServices.rootConfig({
|
||||
data: {
|
||||
backend: {
|
||||
cache: {
|
||||
store: 'memory',
|
||||
},
|
||||
it('rejects invalid defaultTtl', () => {
|
||||
expect(() =>
|
||||
CacheManager.fromConfig(
|
||||
mockServices.rootConfig({
|
||||
data: {
|
||||
backend: {
|
||||
cache: {
|
||||
store: 'memory',
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
).not.toThrow();
|
||||
},
|
||||
}),
|
||||
),
|
||||
).not.toThrow();
|
||||
|
||||
expect(() =>
|
||||
CacheManager.fromConfig(
|
||||
mockServices.rootConfig({
|
||||
data: {
|
||||
backend: {
|
||||
cache: {
|
||||
store: 'memory',
|
||||
defaultTtl: 'hello',
|
||||
},
|
||||
expect(() =>
|
||||
CacheManager.fromConfig(
|
||||
mockServices.rootConfig({
|
||||
data: {
|
||||
backend: {
|
||||
cache: {
|
||||
store: 'memory',
|
||||
defaultTtl: 'hello',
|
||||
},
|
||||
},
|
||||
}),
|
||||
),
|
||||
).toThrow(/Invalid duration 'hello' in config/);
|
||||
});
|
||||
},
|
||||
}),
|
||||
),
|
||||
).toThrow(/Invalid duration 'hello' in config/);
|
||||
});
|
||||
|
||||
describe('CacheManager store options', () => {
|
||||
|
||||
@@ -41,68 +41,65 @@ async function migrateUntilBefore(knex: Knex, target: string): Promise<void> {
|
||||
|
||||
jest.setTimeout(60_000);
|
||||
|
||||
describe('migrations', () => {
|
||||
const databases = TestDatabases.create();
|
||||
const databases = TestDatabases.create();
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'20250411000000_last_run.js, %p',
|
||||
async databaseId => {
|
||||
const knex = await databases.init(databaseId);
|
||||
describe.each(databases.eachSupportedId())('migrations, %p', databaseId => {
|
||||
it('20250411000000_last_run.js', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
|
||||
await migrateUntilBefore(knex, '20250411000000_last_run.js');
|
||||
await migrateUntilBefore(knex, '20250411000000_last_run.js');
|
||||
|
||||
await knex
|
||||
.insert({
|
||||
id: 'i',
|
||||
settings_json: '{}',
|
||||
})
|
||||
.into('backstage_backend_tasks__tasks');
|
||||
await knex
|
||||
.insert({
|
||||
id: 'i',
|
||||
settings_json: '{}',
|
||||
})
|
||||
.into('backstage_backend_tasks__tasks');
|
||||
|
||||
await expect(knex('backstage_backend_tasks__tasks')).resolves.toEqual([
|
||||
{
|
||||
id: 'i',
|
||||
settings_json: '{}',
|
||||
next_run_start_at: null,
|
||||
current_run_ticket: null,
|
||||
current_run_started_at: null,
|
||||
current_run_expires_at: null,
|
||||
},
|
||||
]);
|
||||
await expect(knex('backstage_backend_tasks__tasks')).resolves.toEqual([
|
||||
{
|
||||
id: 'i',
|
||||
settings_json: '{}',
|
||||
next_run_start_at: null,
|
||||
current_run_ticket: null,
|
||||
current_run_started_at: null,
|
||||
current_run_expires_at: null,
|
||||
},
|
||||
]);
|
||||
|
||||
await migrateUpOnce(knex);
|
||||
await migrateUpOnce(knex);
|
||||
|
||||
await knex
|
||||
.table('backstage_backend_tasks__tasks')
|
||||
.update({ last_run_error_json: 'error' })
|
||||
.where({ id: 'i' });
|
||||
await knex
|
||||
.table('backstage_backend_tasks__tasks')
|
||||
.update({ last_run_error_json: 'error' })
|
||||
.where({ id: 'i' });
|
||||
|
||||
await expect(knex('backstage_backend_tasks__tasks')).resolves.toEqual([
|
||||
{
|
||||
id: 'i',
|
||||
settings_json: '{}',
|
||||
next_run_start_at: null,
|
||||
current_run_ticket: null,
|
||||
current_run_started_at: null,
|
||||
current_run_expires_at: null,
|
||||
last_run_ended_at: null,
|
||||
last_run_error_json: 'error',
|
||||
},
|
||||
]);
|
||||
await expect(knex('backstage_backend_tasks__tasks')).resolves.toEqual([
|
||||
{
|
||||
id: 'i',
|
||||
settings_json: '{}',
|
||||
next_run_start_at: null,
|
||||
current_run_ticket: null,
|
||||
current_run_started_at: null,
|
||||
current_run_expires_at: null,
|
||||
last_run_ended_at: null,
|
||||
last_run_error_json: 'error',
|
||||
},
|
||||
]);
|
||||
|
||||
await migrateDownOnce(knex);
|
||||
await migrateDownOnce(knex);
|
||||
|
||||
await expect(knex('backstage_backend_tasks__tasks')).resolves.toEqual([
|
||||
{
|
||||
id: 'i',
|
||||
settings_json: '{}',
|
||||
next_run_start_at: null,
|
||||
current_run_ticket: null,
|
||||
current_run_started_at: null,
|
||||
current_run_expires_at: null,
|
||||
},
|
||||
]);
|
||||
await expect(knex('backstage_backend_tasks__tasks')).resolves.toEqual([
|
||||
{
|
||||
id: 'i',
|
||||
settings_json: '{}',
|
||||
next_run_start_at: null,
|
||||
current_run_ticket: null,
|
||||
current_run_started_at: null,
|
||||
current_run_expires_at: null,
|
||||
},
|
||||
]);
|
||||
|
||||
await knex.destroy();
|
||||
},
|
||||
);
|
||||
await knex.destroy();
|
||||
});
|
||||
});
|
||||
|
||||
+49
-54
@@ -24,9 +24,10 @@ import { metricsServiceMock } from '@backstage/backend-test-utils/alpha';
|
||||
|
||||
jest.setTimeout(60_000);
|
||||
|
||||
describe('TaskScheduler', () => {
|
||||
const databases = TestDatabases.create();
|
||||
|
||||
describe.each(databases.eachSupportedId())('TaskScheduler, %p', databaseId => {
|
||||
const logger = mockServices.logger.mock();
|
||||
const databases = TestDatabases.create();
|
||||
const rootLifecycle = mockServices.rootLifecycle.mock();
|
||||
const httpRouter = mockServices.httpRouter.mock();
|
||||
const pluginMetadata = {
|
||||
@@ -35,63 +36,57 @@ describe('TaskScheduler', () => {
|
||||
const testScopedSignal = createTestScopedSignal();
|
||||
const metrics = metricsServiceMock.mock();
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'can return a working v1 plugin impl, %p',
|
||||
async databaseId => {
|
||||
const knex = await databases.init(databaseId);
|
||||
const database = mockServices.database({ knex });
|
||||
it('can return a working v1 plugin impl', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
const database = mockServices.database({ knex });
|
||||
|
||||
const manager = DefaultSchedulerService.create({
|
||||
database,
|
||||
logger,
|
||||
metrics,
|
||||
rootLifecycle,
|
||||
httpRouter,
|
||||
pluginMetadata,
|
||||
});
|
||||
const fn = jest.fn();
|
||||
const manager = DefaultSchedulerService.create({
|
||||
database,
|
||||
logger,
|
||||
metrics,
|
||||
rootLifecycle,
|
||||
httpRouter,
|
||||
pluginMetadata,
|
||||
});
|
||||
const fn = jest.fn();
|
||||
|
||||
await manager.scheduleTask({
|
||||
id: 'task1',
|
||||
timeout: Duration.fromMillis(5000),
|
||||
frequency: Duration.fromMillis(5000),
|
||||
signal: testScopedSignal(),
|
||||
fn,
|
||||
});
|
||||
await manager.scheduleTask({
|
||||
id: 'task1',
|
||||
timeout: Duration.fromMillis(5000),
|
||||
frequency: Duration.fromMillis(5000),
|
||||
signal: testScopedSignal(),
|
||||
fn,
|
||||
});
|
||||
|
||||
await waitForExpect(() => {
|
||||
expect(fn).toHaveBeenCalled();
|
||||
});
|
||||
},
|
||||
);
|
||||
await waitForExpect(() => {
|
||||
expect(fn).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'can return a working v2 plugin impl, %p',
|
||||
async databaseId => {
|
||||
const knex = await databases.init(databaseId);
|
||||
const database = mockServices.database({ knex });
|
||||
it('can return a working v2 plugin impl', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
const database = mockServices.database({ knex });
|
||||
|
||||
const manager = DefaultSchedulerService.create({
|
||||
database,
|
||||
logger,
|
||||
metrics,
|
||||
rootLifecycle,
|
||||
httpRouter,
|
||||
pluginMetadata,
|
||||
});
|
||||
const fn = jest.fn();
|
||||
const manager = DefaultSchedulerService.create({
|
||||
database,
|
||||
logger,
|
||||
metrics,
|
||||
rootLifecycle,
|
||||
httpRouter,
|
||||
pluginMetadata,
|
||||
});
|
||||
const fn = jest.fn();
|
||||
|
||||
await manager.scheduleTask({
|
||||
id: 'task2',
|
||||
timeout: Duration.fromMillis(5000),
|
||||
frequency: { cron: '* * * * * *' },
|
||||
signal: testScopedSignal(),
|
||||
fn,
|
||||
});
|
||||
await manager.scheduleTask({
|
||||
id: 'task2',
|
||||
timeout: Duration.fromMillis(5000),
|
||||
frequency: { cron: '* * * * * *' },
|
||||
signal: testScopedSignal(),
|
||||
fn,
|
||||
});
|
||||
|
||||
await waitForExpect(() => {
|
||||
expect(fn).toHaveBeenCalled();
|
||||
});
|
||||
},
|
||||
);
|
||||
await waitForExpect(() => {
|
||||
expect(fn).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+220
-246
@@ -31,48 +31,50 @@ import { metricsServiceMock } from '@backstage/backend-test-utils/alpha';
|
||||
|
||||
jest.setTimeout(60_000);
|
||||
|
||||
describe('PluginTaskManagerImpl', () => {
|
||||
const addShutdownHook = jest.fn();
|
||||
const databases = TestDatabases.create({
|
||||
ids: ['POSTGRES_18', 'POSTGRES_14', 'SQLITE_3'],
|
||||
});
|
||||
const databases = TestDatabases.create({
|
||||
ids: ['POSTGRES_18', 'POSTGRES_14', 'SQLITE_3'],
|
||||
});
|
||||
|
||||
beforeAll(async () => {
|
||||
// Make sure all databases are running before mocking timers, in case of testcontainers
|
||||
await Promise.all(
|
||||
databases.eachSupportedId().map(([id]) => databases.init(id)),
|
||||
);
|
||||
describe.each(databases.eachSupportedId())(
|
||||
'PluginTaskManagerImpl, %p',
|
||||
databaseId => {
|
||||
const addShutdownHook = jest.fn();
|
||||
|
||||
jest.useFakeTimers();
|
||||
}, 60_000);
|
||||
beforeAll(async () => {
|
||||
// Make sure the database is running before mocking timers, in case of testcontainers
|
||||
await databases.init(databaseId);
|
||||
jest.useFakeTimers();
|
||||
}, 60_000);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
afterAll(() => {
|
||||
jest.useRealTimers();
|
||||
});
|
||||
|
||||
async function init(databaseId: TestDatabaseId) {
|
||||
const knex = await databases.init(databaseId);
|
||||
await migrateBackendTasks(knex);
|
||||
const manager = new PluginTaskSchedulerImpl(
|
||||
'myplugin',
|
||||
async () => knex,
|
||||
mockServices.logger.mock(),
|
||||
metricsServiceMock.mock(),
|
||||
{
|
||||
addShutdownHook,
|
||||
addBeforeShutdownHook: jest.fn(),
|
||||
addStartupHook: jest.fn(),
|
||||
},
|
||||
);
|
||||
return { knex, manager };
|
||||
}
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
// This is just to test the wrapper code; most of the actual tests are in
|
||||
// TaskWorker.test.ts
|
||||
describe('scheduleTask with global scope', () => {
|
||||
it.each(databases.eachSupportedId())(
|
||||
'can run the v1 happy path, %p',
|
||||
async databaseId => {
|
||||
async function init(id: TestDatabaseId) {
|
||||
const knex = await databases.init(id);
|
||||
await migrateBackendTasks(knex);
|
||||
const manager = new PluginTaskSchedulerImpl(
|
||||
'myplugin',
|
||||
async () => knex,
|
||||
mockServices.logger.mock(),
|
||||
metricsServiceMock.mock(),
|
||||
{
|
||||
addShutdownHook,
|
||||
addBeforeShutdownHook: jest.fn(),
|
||||
addStartupHook: jest.fn(),
|
||||
},
|
||||
);
|
||||
return { knex, manager };
|
||||
}
|
||||
|
||||
// This is just to test the wrapper code; most of the actual tests are in
|
||||
// TaskWorker.test.ts
|
||||
describe('scheduleTask with global scope', () => {
|
||||
it('can run the v1 happy path', async () => {
|
||||
const { manager } = await init(databaseId);
|
||||
|
||||
const fn = jest.fn();
|
||||
@@ -87,12 +89,9 @@ describe('PluginTaskManagerImpl', () => {
|
||||
|
||||
await promise;
|
||||
expect(fn).toHaveBeenCalledWith(expect.any(AbortSignal));
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'can run the v2 happy path, %p',
|
||||
async databaseId => {
|
||||
it('can run the v2 happy path', async () => {
|
||||
const { manager } = await init(databaseId);
|
||||
|
||||
const fn = jest.fn();
|
||||
@@ -107,12 +106,9 @@ describe('PluginTaskManagerImpl', () => {
|
||||
|
||||
await promise;
|
||||
expect(fn).toHaveBeenCalledWith(expect.any(AbortSignal));
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'aborts the task if shutdown hook is invoked, %p',
|
||||
async databaseId => {
|
||||
it('aborts the task if shutdown hook is invoked', async () => {
|
||||
const { manager } = await init(databaseId);
|
||||
|
||||
const fn = jest.fn();
|
||||
@@ -134,14 +130,11 @@ describe('PluginTaskManagerImpl', () => {
|
||||
// Should be aborted after the shutdown hook is invoked
|
||||
await shutdownHook();
|
||||
expect(abortSignal.aborted).toBe(true);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('triggerTask with global scope', () => {
|
||||
it.each(databases.eachSupportedId())(
|
||||
'can manually trigger a task, %p',
|
||||
async databaseId => {
|
||||
describe('triggerTask with global scope', () => {
|
||||
it('can manually trigger a task', async () => {
|
||||
const { manager } = await init(databaseId);
|
||||
|
||||
const fn = jest.fn();
|
||||
@@ -160,12 +153,9 @@ describe('PluginTaskManagerImpl', () => {
|
||||
|
||||
await promise;
|
||||
expect(fn).toHaveBeenCalledWith(expect.any(AbortSignal));
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'cant trigger a non-existent task, %p',
|
||||
async databaseId => {
|
||||
it('cant trigger a non-existent task', async () => {
|
||||
const { manager } = await init(databaseId);
|
||||
|
||||
const fn = jest.fn();
|
||||
@@ -180,12 +170,9 @@ describe('PluginTaskManagerImpl', () => {
|
||||
await expect(() => manager.triggerTask('task2')).rejects.toThrow(
|
||||
NotFoundError,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'cant trigger a running task, %p',
|
||||
async databaseId => {
|
||||
it('cant trigger a running task', async () => {
|
||||
const { manager } = await init(databaseId);
|
||||
|
||||
const promise = createDeferred();
|
||||
@@ -205,140 +192,137 @@ describe('PluginTaskManagerImpl', () => {
|
||||
await expect(() => manager.triggerTask('task1')).rejects.toThrow(
|
||||
ConflictError,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
// This is just to test the wrapper code; most of the actual tests are in
|
||||
// TaskWorker.test.ts
|
||||
describe('scheduleTask with local scope', () => {
|
||||
it('can run the v1 happy path', async () => {
|
||||
const { manager } = await init('SQLITE_3');
|
||||
|
||||
const fn = jest.fn();
|
||||
const promise = new Promise(resolve => fn.mockImplementation(resolve));
|
||||
await manager.scheduleTask({
|
||||
id: 'task1',
|
||||
timeout: { milliseconds: 5000 },
|
||||
frequency: { milliseconds: 5000 },
|
||||
fn,
|
||||
scope: 'local',
|
||||
});
|
||||
});
|
||||
|
||||
await promise;
|
||||
expect(fn).toHaveBeenCalledWith(expect.any(AbortSignal));
|
||||
}, 60_000);
|
||||
// This is just to test the wrapper code; most of the actual tests are in
|
||||
// TaskWorker.test.ts
|
||||
describe('scheduleTask with local scope', () => {
|
||||
it('can run the v1 happy path', async () => {
|
||||
const { manager } = await init('SQLITE_3');
|
||||
|
||||
it('can run the v2 happy path', async () => {
|
||||
const { manager } = await init('SQLITE_3');
|
||||
const fn = jest.fn();
|
||||
const promise = new Promise(resolve => fn.mockImplementation(resolve));
|
||||
await manager.scheduleTask({
|
||||
id: 'task1',
|
||||
timeout: { milliseconds: 5000 },
|
||||
frequency: { milliseconds: 5000 },
|
||||
fn,
|
||||
scope: 'local',
|
||||
});
|
||||
|
||||
const fn = jest.fn();
|
||||
const promise = new Promise(resolve => fn.mockImplementation(resolve));
|
||||
await manager.scheduleTask({
|
||||
id: 'task2',
|
||||
timeout: Duration.fromMillis(5000),
|
||||
frequency: { cron: '* * * * * *' },
|
||||
fn,
|
||||
scope: 'local',
|
||||
});
|
||||
await promise;
|
||||
expect(fn).toHaveBeenCalledWith(expect.any(AbortSignal));
|
||||
}, 60_000);
|
||||
|
||||
await promise;
|
||||
expect(fn).toHaveBeenCalledWith(expect.any(AbortSignal));
|
||||
}, 60_000);
|
||||
it('can run the v2 happy path', async () => {
|
||||
const { manager } = await init('SQLITE_3');
|
||||
|
||||
it('aborts the task if shutdown hook is invoked', async () => {
|
||||
const { manager } = await init('SQLITE_3');
|
||||
const fn = jest.fn();
|
||||
const promise = new Promise(resolve => fn.mockImplementation(resolve));
|
||||
await manager.scheduleTask({
|
||||
id: 'task2',
|
||||
timeout: Duration.fromMillis(5000),
|
||||
frequency: { cron: '* * * * * *' },
|
||||
fn,
|
||||
scope: 'local',
|
||||
});
|
||||
|
||||
const fn = jest.fn();
|
||||
const promise = new Promise<AbortSignal>(resolve =>
|
||||
fn.mockImplementation(resolve),
|
||||
);
|
||||
await manager.scheduleTask({
|
||||
id: 'task3',
|
||||
timeout: Duration.fromMillis(5000),
|
||||
frequency: { cron: '* * * * * *' },
|
||||
fn,
|
||||
scope: 'local',
|
||||
});
|
||||
await promise;
|
||||
expect(fn).toHaveBeenCalledWith(expect.any(AbortSignal));
|
||||
}, 60_000);
|
||||
|
||||
const shutdownHook = addShutdownHook.mock.calls[0][0];
|
||||
const abortSignal = await promise;
|
||||
expect(abortSignal.aborted).toBe(false);
|
||||
it('aborts the task if shutdown hook is invoked', async () => {
|
||||
const { manager } = await init('SQLITE_3');
|
||||
|
||||
// Should be aborted after the shutdown hook is invoked
|
||||
await shutdownHook();
|
||||
expect(abortSignal.aborted).toBe(true);
|
||||
}, 60_000);
|
||||
});
|
||||
const fn = jest.fn();
|
||||
const promise = new Promise<AbortSignal>(resolve =>
|
||||
fn.mockImplementation(resolve),
|
||||
);
|
||||
await manager.scheduleTask({
|
||||
id: 'task3',
|
||||
timeout: Duration.fromMillis(5000),
|
||||
frequency: { cron: '* * * * * *' },
|
||||
fn,
|
||||
scope: 'local',
|
||||
});
|
||||
|
||||
describe('triggerTask with local scope', () => {
|
||||
it('can manually trigger a task', async () => {
|
||||
const { manager } = await init('SQLITE_3');
|
||||
const shutdownHook = addShutdownHook.mock.calls[0][0];
|
||||
const abortSignal = await promise;
|
||||
expect(abortSignal.aborted).toBe(false);
|
||||
|
||||
const fn = jest.fn();
|
||||
const promise = new Promise(resolve => fn.mockImplementation(resolve));
|
||||
await manager.scheduleTask({
|
||||
id: 'task1',
|
||||
timeout: Duration.fromMillis(5000),
|
||||
frequency: Duration.fromObject({ years: 1 }),
|
||||
initialDelay: Duration.fromObject({ years: 1 }),
|
||||
fn,
|
||||
scope: 'local',
|
||||
});
|
||||
// Should be aborted after the shutdown hook is invoked
|
||||
await shutdownHook();
|
||||
expect(abortSignal.aborted).toBe(true);
|
||||
}, 60_000);
|
||||
});
|
||||
|
||||
await manager.triggerTask('task1');
|
||||
jest.advanceTimersByTime(5000);
|
||||
describe('triggerTask with local scope', () => {
|
||||
it('can manually trigger a task', async () => {
|
||||
const { manager } = await init('SQLITE_3');
|
||||
|
||||
await promise;
|
||||
expect(fn).toHaveBeenCalledWith(expect.any(AbortSignal));
|
||||
}, 60_000);
|
||||
const fn = jest.fn();
|
||||
const promise = new Promise(resolve => fn.mockImplementation(resolve));
|
||||
await manager.scheduleTask({
|
||||
id: 'task1',
|
||||
timeout: Duration.fromMillis(5000),
|
||||
frequency: Duration.fromObject({ years: 1 }),
|
||||
initialDelay: Duration.fromObject({ years: 1 }),
|
||||
fn,
|
||||
scope: 'local',
|
||||
});
|
||||
|
||||
it('cant trigger a non-existent task', async () => {
|
||||
const { manager } = await init('SQLITE_3');
|
||||
await manager.triggerTask('task1');
|
||||
jest.advanceTimersByTime(5000);
|
||||
|
||||
const fn = jest.fn();
|
||||
await manager.scheduleTask({
|
||||
id: 'task1',
|
||||
timeout: Duration.fromMillis(5000),
|
||||
frequency: Duration.fromObject({ years: 1 }),
|
||||
fn,
|
||||
scope: 'local',
|
||||
});
|
||||
await promise;
|
||||
expect(fn).toHaveBeenCalledWith(expect.any(AbortSignal));
|
||||
}, 60_000);
|
||||
|
||||
await expect(() => manager.triggerTask('task2')).rejects.toThrow(
|
||||
NotFoundError,
|
||||
);
|
||||
}, 60_000);
|
||||
it('cant trigger a non-existent task', async () => {
|
||||
const { manager } = await init('SQLITE_3');
|
||||
|
||||
it('cant trigger a running task', async () => {
|
||||
const { manager } = await init('SQLITE_3');
|
||||
const fn = jest.fn();
|
||||
await manager.scheduleTask({
|
||||
id: 'task1',
|
||||
timeout: Duration.fromMillis(5000),
|
||||
frequency: Duration.fromObject({ years: 1 }),
|
||||
fn,
|
||||
scope: 'local',
|
||||
});
|
||||
|
||||
const promise = createDeferred();
|
||||
await expect(() => manager.triggerTask('task2')).rejects.toThrow(
|
||||
NotFoundError,
|
||||
);
|
||||
}, 60_000);
|
||||
|
||||
await manager.scheduleTask({
|
||||
id: 'task1',
|
||||
timeout: Duration.fromMillis(5000),
|
||||
frequency: Duration.fromObject({ years: 1 }),
|
||||
fn: async () => {
|
||||
promise.resolve();
|
||||
await new Promise(r => setTimeout(r, 20000));
|
||||
},
|
||||
scope: 'local',
|
||||
});
|
||||
it('cant trigger a running task', async () => {
|
||||
const { manager } = await init('SQLITE_3');
|
||||
|
||||
await promise;
|
||||
await expect(() => manager.triggerTask('task1')).rejects.toThrow(
|
||||
ConflictError,
|
||||
);
|
||||
}, 60_000);
|
||||
});
|
||||
const promise = createDeferred();
|
||||
|
||||
// This is just to test the wrapper code; most of the actual tests are in
|
||||
// TaskWorker.test.ts
|
||||
describe('createScheduledTaskRunner', () => {
|
||||
it.each(databases.eachSupportedId())(
|
||||
'can run the happy path, %p',
|
||||
async databaseId => {
|
||||
await manager.scheduleTask({
|
||||
id: 'task1',
|
||||
timeout: Duration.fromMillis(5000),
|
||||
frequency: Duration.fromObject({ years: 1 }),
|
||||
fn: async () => {
|
||||
promise.resolve();
|
||||
await new Promise(r => setTimeout(r, 20000));
|
||||
},
|
||||
scope: 'local',
|
||||
});
|
||||
|
||||
await promise;
|
||||
await expect(() => manager.triggerTask('task1')).rejects.toThrow(
|
||||
ConflictError,
|
||||
);
|
||||
}, 60_000);
|
||||
});
|
||||
|
||||
// This is just to test the wrapper code; most of the actual tests are in
|
||||
// TaskWorker.test.ts
|
||||
describe('createScheduledTaskRunner', () => {
|
||||
it('can run the happy path', async () => {
|
||||
const { manager } = await init(databaseId);
|
||||
|
||||
const fn = jest.fn();
|
||||
@@ -356,14 +340,11 @@ describe('PluginTaskManagerImpl', () => {
|
||||
|
||||
await promise;
|
||||
expect(fn).toHaveBeenCalledWith(expect.any(AbortSignal));
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('can fetch task ids', () => {
|
||||
it.each(databases.eachSupportedId())(
|
||||
'can fetch both global and local task ids, %p',
|
||||
async databaseId => {
|
||||
describe('can fetch task ids', () => {
|
||||
it('can fetch both global and local task ids', async () => {
|
||||
const { manager } = await init(databaseId);
|
||||
const fn = jest.fn();
|
||||
|
||||
@@ -395,52 +376,51 @@ describe('PluginTaskManagerImpl', () => {
|
||||
settings: expect.objectContaining({ cadence: 'PT5S' }),
|
||||
},
|
||||
]);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('cancelTask with local scope', () => {
|
||||
it('can cancel a running task', async () => {
|
||||
const { manager } = await init('SQLITE_3');
|
||||
|
||||
const promise = createDeferred();
|
||||
|
||||
await manager.scheduleTask({
|
||||
id: 'task1',
|
||||
timeout: Duration.fromMillis(5000),
|
||||
frequency: Duration.fromObject({ years: 1 }),
|
||||
fn: async () => {
|
||||
promise.resolve();
|
||||
await new Promise(r => setTimeout(r, 20000));
|
||||
},
|
||||
scope: 'local',
|
||||
});
|
||||
});
|
||||
|
||||
await promise;
|
||||
await expect(manager.cancelTask('task1')).resolves.toBeUndefined();
|
||||
}, 60_000);
|
||||
describe('cancelTask with local scope', () => {
|
||||
it('can cancel a running task', async () => {
|
||||
const { manager } = await init('SQLITE_3');
|
||||
|
||||
it('cannot cancel a task that is not running', async () => {
|
||||
const { manager } = await init('SQLITE_3');
|
||||
const promise = createDeferred();
|
||||
|
||||
const fn = jest.fn();
|
||||
await manager.scheduleTask({
|
||||
id: 'task1',
|
||||
timeout: Duration.fromMillis(5000),
|
||||
frequency: Duration.fromObject({ years: 1 }),
|
||||
initialDelay: Duration.fromObject({ years: 1 }),
|
||||
fn,
|
||||
scope: 'local',
|
||||
});
|
||||
await manager.scheduleTask({
|
||||
id: 'task1',
|
||||
timeout: Duration.fromMillis(5000),
|
||||
frequency: Duration.fromObject({ years: 1 }),
|
||||
fn: async () => {
|
||||
promise.resolve();
|
||||
await new Promise(r => setTimeout(r, 20000));
|
||||
},
|
||||
scope: 'local',
|
||||
});
|
||||
|
||||
await expect(manager.cancelTask('task1')).rejects.toThrow(ConflictError);
|
||||
}, 60_000);
|
||||
});
|
||||
await promise;
|
||||
await expect(manager.cancelTask('task1')).resolves.toBeUndefined();
|
||||
}, 60_000);
|
||||
|
||||
describe('cancelTask with global scope', () => {
|
||||
it.each(databases.eachSupportedId())(
|
||||
'can cancel a running task, %p',
|
||||
async databaseId => {
|
||||
it('cannot cancel a task that is not running', async () => {
|
||||
const { manager } = await init('SQLITE_3');
|
||||
|
||||
const fn = jest.fn();
|
||||
await manager.scheduleTask({
|
||||
id: 'task1',
|
||||
timeout: Duration.fromMillis(5000),
|
||||
frequency: Duration.fromObject({ years: 1 }),
|
||||
initialDelay: Duration.fromObject({ years: 1 }),
|
||||
fn,
|
||||
scope: 'local',
|
||||
});
|
||||
|
||||
await expect(manager.cancelTask('task1')).rejects.toThrow(
|
||||
ConflictError,
|
||||
);
|
||||
}, 60_000);
|
||||
});
|
||||
|
||||
describe('cancelTask with global scope', () => {
|
||||
it('can cancel a running task', async () => {
|
||||
const { manager } = await init(databaseId);
|
||||
|
||||
const promise = createDeferred();
|
||||
@@ -458,23 +438,17 @@ describe('PluginTaskManagerImpl', () => {
|
||||
|
||||
await promise;
|
||||
await expect(manager.cancelTask('task1')).resolves.toBeUndefined();
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'cannot cancel a non-existent task, %p',
|
||||
async databaseId => {
|
||||
it('cannot cancel a non-existent task', async () => {
|
||||
const { manager } = await init(databaseId);
|
||||
|
||||
await expect(manager.cancelTask('nonexistent')).rejects.toThrow(
|
||||
NotFoundError,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'cannot cancel a task that is not running, %p',
|
||||
async databaseId => {
|
||||
it('cannot cancel a task that is not running', async () => {
|
||||
const { manager } = await init(databaseId);
|
||||
|
||||
await manager.scheduleTask({
|
||||
@@ -489,16 +463,16 @@ describe('PluginTaskManagerImpl', () => {
|
||||
await expect(manager.cancelTask('task1')).rejects.toThrow(
|
||||
ConflictError,
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('parseDuration', () => {
|
||||
it('should parse durations', () => {
|
||||
expect(parseDuration({ milliseconds: 5000 })).toEqual('PT5S');
|
||||
expect(parseDuration(Duration.fromMillis(5000))).toEqual('PT5S');
|
||||
expect(parseDuration({ cron: '1 * * * *' })).toEqual('1 * * * *');
|
||||
expect(parseDuration({ trigger: 'manual' })).toEqual('manual');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseDuration', () => {
|
||||
it('should parse durations', () => {
|
||||
expect(parseDuration({ milliseconds: 5000 })).toEqual('PT5S');
|
||||
expect(parseDuration(Duration.fromMillis(5000))).toEqual('PT5S');
|
||||
expect(parseDuration({ cron: '1 * * * *' })).toEqual('1 * * * *');
|
||||
expect(parseDuration({ trigger: 'manual' })).toEqual('manual');
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
+23
-22
@@ -34,28 +34,29 @@ const getTask = async (knex: Knex): Promise<DbTasksRow> => {
|
||||
return (await knex<DbTasksRow>(DB_TASKS_TABLE))[0];
|
||||
};
|
||||
|
||||
describe('PluginTaskSchedulerJanitor', () => {
|
||||
const logger = mockServices.logger.mock();
|
||||
const databases = TestDatabases.create({
|
||||
ids: [
|
||||
/* 'MYSQL_8' not supported yet */
|
||||
'POSTGRES_18',
|
||||
'POSTGRES_14',
|
||||
'SQLITE_3',
|
||||
'MYSQL_8',
|
||||
],
|
||||
});
|
||||
const testScopedSignal = createTestScopedSignal();
|
||||
jest.setTimeout(60_000);
|
||||
|
||||
jest.setTimeout(60_000);
|
||||
const databases = TestDatabases.create({
|
||||
ids: [
|
||||
/* 'MYSQL_8' not supported yet */
|
||||
'POSTGRES_18',
|
||||
'POSTGRES_14',
|
||||
'SQLITE_3',
|
||||
'MYSQL_8',
|
||||
],
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
describe.each(databases.eachSupportedId())(
|
||||
'PluginTaskSchedulerJanitor, %p',
|
||||
databaseId => {
|
||||
const logger = mockServices.logger.mock();
|
||||
const testScopedSignal = createTestScopedSignal();
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'Should update date if current_run_expires_at expires, %p',
|
||||
async databaseId => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('Should update date if current_run_expires_at expires', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
await migrateBackendTasks(knex);
|
||||
|
||||
@@ -92,6 +93,6 @@ describe('PluginTaskSchedulerJanitor', () => {
|
||||
}),
|
||||
);
|
||||
});
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
@@ -41,83 +41,77 @@ async function migrateUntilBefore(knex: Knex, target: string): Promise<void> {
|
||||
|
||||
jest.setTimeout(60_000);
|
||||
|
||||
describe('migrations', () => {
|
||||
const databases = TestDatabases.create();
|
||||
const databases = TestDatabases.create();
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'20210928160613_init.js, %p',
|
||||
async databaseId => {
|
||||
const knex = await databases.init(databaseId);
|
||||
describe.each(databases.eachSupportedId())('migrations, %p', databaseId => {
|
||||
it('20210928160613_init.js', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
|
||||
await migrateUntilBefore(knex, '20210928160613_init.js');
|
||||
await migrateUpOnce(knex);
|
||||
await migrateUntilBefore(knex, '20210928160613_init.js');
|
||||
await migrateUpOnce(knex);
|
||||
|
||||
await knex('backstage_backend_tasks__tasks').insert({
|
||||
await knex('backstage_backend_tasks__tasks').insert({
|
||||
id: 'test',
|
||||
settings_json: '{}',
|
||||
next_run_start_at: knex.fn.now(),
|
||||
});
|
||||
|
||||
await expect(knex('backstage_backend_tasks__tasks')).resolves.toEqual([
|
||||
{
|
||||
id: 'test',
|
||||
settings_json: '{}',
|
||||
next_run_start_at: knex.fn.now(),
|
||||
});
|
||||
next_run_start_at: expect.anything(),
|
||||
current_run_ticket: null,
|
||||
current_run_started_at: null,
|
||||
current_run_expires_at: null,
|
||||
},
|
||||
]);
|
||||
|
||||
await expect(knex('backstage_backend_tasks__tasks')).resolves.toEqual([
|
||||
{
|
||||
id: 'test',
|
||||
settings_json: '{}',
|
||||
next_run_start_at: expect.anything(),
|
||||
current_run_ticket: null,
|
||||
current_run_started_at: null,
|
||||
current_run_expires_at: null,
|
||||
},
|
||||
]);
|
||||
await migrateDownOnce(knex);
|
||||
|
||||
await migrateDownOnce(knex);
|
||||
// This looks odd - you might expect a .toThrow at the end but that
|
||||
// actually is flaky for some reason specifically on sqlite when
|
||||
// performing multiple runs in sequence
|
||||
await expect(knex('backstage_backend_tasks__tasks')).rejects.toEqual(
|
||||
expect.anything(),
|
||||
);
|
||||
|
||||
// This looks odd - you might expect a .toThrow at the end but that
|
||||
// actually is flaky for some reason specifically on sqlite when
|
||||
// performing multiple runs in sequence
|
||||
await expect(knex('backstage_backend_tasks__tasks')).rejects.toEqual(
|
||||
expect.anything(),
|
||||
);
|
||||
await knex.destroy();
|
||||
});
|
||||
|
||||
await knex.destroy();
|
||||
},
|
||||
);
|
||||
it('20240712211735_nullable_next_run.js', async () => {
|
||||
const knex = await databases.init(databaseId);
|
||||
|
||||
it.each(databases.eachSupportedId())(
|
||||
'20240712211735_nullable_next_run.js, %p',
|
||||
async databaseId => {
|
||||
const knex = await databases.init(databaseId);
|
||||
await migrateUntilBefore(knex, '20240712211735_nullable_next_run.js');
|
||||
await migrateUpOnce(knex);
|
||||
|
||||
await migrateUntilBefore(knex, '20240712211735_nullable_next_run.js');
|
||||
await migrateUpOnce(knex);
|
||||
await knex('backstage_backend_tasks__tasks').insert({
|
||||
id: 'test',
|
||||
settings_json: '{}',
|
||||
next_run_start_at: knex.raw('null'),
|
||||
});
|
||||
|
||||
await knex('backstage_backend_tasks__tasks').insert({
|
||||
await expect(knex('backstage_backend_tasks__tasks')).resolves.toEqual([
|
||||
{
|
||||
id: 'test',
|
||||
settings_json: '{}',
|
||||
next_run_start_at: null,
|
||||
current_run_ticket: null,
|
||||
current_run_started_at: null,
|
||||
current_run_expires_at: null,
|
||||
},
|
||||
]);
|
||||
|
||||
await migrateDownOnce(knex);
|
||||
|
||||
await expect(
|
||||
knex('backstage_backend_tasks__tasks').insert({
|
||||
id: 'test',
|
||||
settings_json: '{}',
|
||||
next_run_start_at: knex.raw('null'),
|
||||
});
|
||||
}),
|
||||
).rejects.toEqual(expect.anything());
|
||||
|
||||
await expect(knex('backstage_backend_tasks__tasks')).resolves.toEqual([
|
||||
{
|
||||
id: 'test',
|
||||
settings_json: '{}',
|
||||
next_run_start_at: null,
|
||||
current_run_ticket: null,
|
||||
current_run_started_at: null,
|
||||
current_run_expires_at: null,
|
||||
},
|
||||
]);
|
||||
|
||||
await migrateDownOnce(knex);
|
||||
|
||||
await expect(
|
||||
knex('backstage_backend_tasks__tasks').insert({
|
||||
id: 'test',
|
||||
settings_json: '{}',
|
||||
next_run_start_at: knex.raw('null'),
|
||||
}),
|
||||
).rejects.toEqual(expect.anything());
|
||||
|
||||
await knex.destroy();
|
||||
},
|
||||
);
|
||||
await knex.destroy();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -21,10 +21,10 @@ const itIfDocker = isDockerDisabledForTests() ? it.skip : it;
|
||||
|
||||
jest.setTimeout(60_000);
|
||||
|
||||
describe('TestCaches', () => {
|
||||
const caches = TestCaches.create();
|
||||
const caches = TestCaches.create();
|
||||
|
||||
it.each(caches.eachSupportedId())('fires up a cache, %p', async cacheId => {
|
||||
describe.each(caches.eachSupportedId())('TestCaches, %p', cacheId => {
|
||||
it('fires up a cache', async () => {
|
||||
const { keyv } = await caches.init(cacheId);
|
||||
await keyv.set('test', 'value');
|
||||
await expect(keyv.get('test')).resolves.toBe('value');
|
||||
|
||||
@@ -18,24 +18,14 @@ import { TestDatabases } from './TestDatabases';
|
||||
|
||||
jest.setTimeout(60_000);
|
||||
|
||||
describe('TestDatabases', () => {
|
||||
describe('each create', () => {
|
||||
const dbs = TestDatabases.create();
|
||||
const dbs = TestDatabases.create();
|
||||
|
||||
it.each(dbs.eachSupportedId())(
|
||||
'creates distinct %p databases',
|
||||
async databaseId => {
|
||||
if (!dbs.supports(databaseId)) {
|
||||
return;
|
||||
}
|
||||
const db1 = await dbs.init(databaseId);
|
||||
const db2 = await dbs.init(databaseId);
|
||||
await db1.schema.createTable('a', table => table.string('x').primary());
|
||||
await db2.schema.createTable('a', table => table.string('y').primary());
|
||||
await expect(db1.select({ a: db1.raw('1') })).resolves.toEqual([
|
||||
{ a: 1 },
|
||||
]);
|
||||
},
|
||||
);
|
||||
describe.each(dbs.eachSupportedId())('TestDatabases, %p', databaseId => {
|
||||
it('creates distinct databases', async () => {
|
||||
const db1 = await dbs.init(databaseId);
|
||||
const db2 = await dbs.init(databaseId);
|
||||
await db1.schema.createTable('a', table => table.string('x').primary());
|
||||
await db2.schema.createTable('a', table => table.string('y').primary());
|
||||
await expect(db1.select({ a: db1.raw('1') })).resolves.toEqual([{ a: 1 }]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user