Filtered upgrade logs when stopping before starting next instance segment (#20078)
# Introduction In a nutshell, added a more readable logs that relates that when performing a workspace fitlered workspace you can only browser the workspace commands sequence This PR is not a fix, but a log improvement As before when cross-upgrading a single workspace you would be facing a `instance` sync barrier error as not all your workspaces would have been updated Now logging and early returning instead of letting the guard hard throw ## Tests Created a dedicated test for instance prevention on filtered upgrade Standardized `migrationRecordToKey` usage across all upgrade integration tests suites
This commit is contained in:
+13
@@ -68,6 +68,19 @@ export class UpgradeSequenceRunnerService {
|
||||
const step = sequence[cursor];
|
||||
|
||||
if (step.kind === 'fast-instance' || step.kind === 'slow-instance') {
|
||||
if (
|
||||
isDefined(options.workspaceIds) &&
|
||||
options.workspaceIds.length > 0
|
||||
) {
|
||||
this.logger.log(
|
||||
`Stopping before instance step "${step.name}": ` +
|
||||
'upgrade was run with workspace filter (-w). ' +
|
||||
'Instance commands require all workspaces to be aligned.',
|
||||
);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
const previousStep = cursor > 0 ? sequence[cursor - 1] : undefined;
|
||||
|
||||
if (previousStep?.kind === 'workspace') {
|
||||
|
||||
+42
-56
@@ -10,11 +10,12 @@ import {
|
||||
makeSlowInstance,
|
||||
makeStep,
|
||||
makeWorkspace,
|
||||
migrationRecordToKey,
|
||||
resetSeedSequenceCounter,
|
||||
seedInstanceMigration,
|
||||
seedWorkspaceMigration,
|
||||
setMockActiveWorkspaceIds,
|
||||
testGetLatestMigrationForCommand,
|
||||
testGetExecutedMigrationsInOrder,
|
||||
WS_1,
|
||||
WS_2,
|
||||
} from 'test/integration/upgrade/utils/upgrade-sequence-runner-integration-test.util';
|
||||
@@ -198,13 +199,15 @@ describe('UpgradeSequenceRunnerService — failing sequence (integration)', () =
|
||||
}),
|
||||
).rejects.toThrow('fast command exploded');
|
||||
|
||||
const ic2 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Ic2',
|
||||
});
|
||||
const executed = await testGetExecutedMigrationsInOrder(context.dataSource);
|
||||
|
||||
expect(ic2).toEqual(
|
||||
expect.objectContaining({ name: 'Ic2', status: 'failed' }),
|
||||
);
|
||||
expect(executed.map(migrationRecordToKey)).toStrictEqual([
|
||||
// Seeds
|
||||
'Ic1:instance:completed:1',
|
||||
|
||||
// Ic2 attempted and failed
|
||||
'Ic2:instance:failed:1',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should record failure in DB when a slow instance command fails', async () => {
|
||||
@@ -238,13 +241,17 @@ describe('UpgradeSequenceRunnerService — failing sequence (integration)', () =
|
||||
}),
|
||||
).rejects.toThrow('slow data migration exploded');
|
||||
|
||||
const ic2 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Ic2',
|
||||
});
|
||||
const executed = await testGetExecutedMigrationsInOrder(context.dataSource);
|
||||
|
||||
expect(ic2).toEqual(
|
||||
expect.objectContaining({ name: 'Ic2', status: 'failed' }),
|
||||
);
|
||||
expect(executed.map(migrationRecordToKey)).toStrictEqual([
|
||||
// Seeds
|
||||
'Ic1:instance:completed:1',
|
||||
`Ic1:${WS_1}:completed:1`,
|
||||
|
||||
// Ic2 attempted and failed
|
||||
'Ic2:instance:failed:1',
|
||||
`Ic2:${WS_1}:failed:1`,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should abort and report failures when workspace commands fail, without running subsequent instance steps', async () => {
|
||||
@@ -270,20 +277,15 @@ describe('UpgradeSequenceRunnerService — failing sequence (integration)', () =
|
||||
expect(report.totalFailures).toBe(1);
|
||||
expect(report.totalSuccesses).toBe(0);
|
||||
|
||||
const ic1 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Ic1',
|
||||
});
|
||||
const executed = await testGetExecutedMigrationsInOrder(context.dataSource);
|
||||
|
||||
expect(ic1).toBeNull();
|
||||
expect(executed.map(migrationRecordToKey)).toStrictEqual([
|
||||
// Seeds
|
||||
`Wc1:${WS_1}:failed:1`,
|
||||
|
||||
const wc1 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Wc1',
|
||||
workspaceId: WS_1,
|
||||
});
|
||||
|
||||
expect(wc1).toEqual(
|
||||
expect.objectContaining({ status: 'failed', attempt: 2 }),
|
||||
);
|
||||
// Runner retries Wc1, still fails — Ic1 never reached
|
||||
`Wc1:${WS_1}:failed:2`,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should abort at workspace failure in a multi-segment sequence with two workspaces starting aligned', async () => {
|
||||
@@ -337,39 +339,23 @@ describe('UpgradeSequenceRunnerService — failing sequence (integration)', () =
|
||||
expect(report.totalSuccesses).toBe(1);
|
||||
expect(report.totalFailures).toBe(1);
|
||||
|
||||
// WS_1 succeeded Wc2
|
||||
const ws1Wc2 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Wc2',
|
||||
workspaceId: WS_1,
|
||||
});
|
||||
const executed = await testGetExecutedMigrationsInOrder(context.dataSource);
|
||||
|
||||
expect(ws1Wc2).toEqual(
|
||||
expect.objectContaining({ name: 'Wc2', status: 'completed' }),
|
||||
);
|
||||
expect(executed.map(migrationRecordToKey)).toStrictEqual([
|
||||
// Seeds
|
||||
`Wc0:${WS_1}:completed:1`,
|
||||
`Wc0:${WS_2}:completed:1`,
|
||||
'Ic1:instance:completed:1',
|
||||
`Ic1:${WS_1}:completed:1`,
|
||||
`Ic1:${WS_2}:completed:1`,
|
||||
`Wc1:${WS_1}:completed:1`,
|
||||
`Wc1:${WS_2}:completed:1`,
|
||||
|
||||
// WS_2 failed Wc2
|
||||
const ws2Wc2 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Wc2',
|
||||
workspaceId: WS_2,
|
||||
});
|
||||
// WS_1 succeeds Wc2, WS_2 fails Wc2
|
||||
`Wc2:${WS_1}:completed:1`,
|
||||
`Wc2:${WS_2}:failed:1`,
|
||||
|
||||
expect(ws2Wc2).toEqual(
|
||||
expect.objectContaining({ name: 'Wc2', status: 'failed' }),
|
||||
);
|
||||
|
||||
// Ic2 never ran — runner aborted at the workspace segment failure
|
||||
const ic2 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Ic2',
|
||||
});
|
||||
|
||||
expect(ic2).toBeNull();
|
||||
|
||||
// Wc3 never ran either
|
||||
const ws1Wc3 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Wc3',
|
||||
workspaceId: WS_1,
|
||||
});
|
||||
|
||||
expect(ws1Wc3).toBeNull();
|
||||
// Ic2 and Wc3 never reached — runner aborted at workspace failure
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
+159
-112
@@ -7,11 +7,12 @@ import {
|
||||
makeFastInstance,
|
||||
makeSlowInstance,
|
||||
makeWorkspace,
|
||||
migrationRecordToKey,
|
||||
resetSeedSequenceCounter,
|
||||
seedInstanceMigration,
|
||||
seedWorkspaceMigration,
|
||||
setMockActiveWorkspaceIds,
|
||||
testGetLatestMigrationForCommand,
|
||||
testGetExecutedMigrationsInOrder,
|
||||
WS_1,
|
||||
WS_2,
|
||||
} from 'test/integration/upgrade/utils/upgrade-sequence-runner-integration-test.util';
|
||||
@@ -66,23 +67,16 @@ describe('UpgradeSequenceRunnerService — execution (integration)', () => {
|
||||
options: DEFAULT_OPTIONS,
|
||||
});
|
||||
|
||||
const ic1 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Ic1',
|
||||
});
|
||||
const ic2 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Ic2',
|
||||
});
|
||||
const ic3 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Ic3',
|
||||
});
|
||||
const executed = await testGetExecutedMigrationsInOrder(context.dataSource);
|
||||
|
||||
expect(ic1).toEqual(
|
||||
expect.objectContaining({ status: 'completed', attempt: 1 }),
|
||||
);
|
||||
expect(ic2).toEqual(
|
||||
expect.objectContaining({ status: 'completed', attempt: 1 }),
|
||||
);
|
||||
expect(ic3).toEqual(expect.objectContaining({ status: 'completed' }));
|
||||
expect(executed.map(migrationRecordToKey)).toStrictEqual([
|
||||
// Seeds
|
||||
'Ic1:instance:completed:1',
|
||||
'Ic2:instance:completed:1',
|
||||
|
||||
// Runner resumes after Ic2, runs Ic3
|
||||
'Ic3:instance:completed:1',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should retry a failed instance command', async () => {
|
||||
@@ -102,13 +96,16 @@ describe('UpgradeSequenceRunnerService — execution (integration)', () => {
|
||||
options: DEFAULT_OPTIONS,
|
||||
});
|
||||
|
||||
const ic2 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Ic2',
|
||||
});
|
||||
const executed = await testGetExecutedMigrationsInOrder(context.dataSource);
|
||||
|
||||
expect(ic2).toEqual(
|
||||
expect.objectContaining({ name: 'Ic2', status: 'completed', attempt: 2 }),
|
||||
);
|
||||
expect(executed.map(migrationRecordToKey)).toStrictEqual([
|
||||
// Seeds
|
||||
'Ic1:instance:completed:1',
|
||||
'Ic2:instance:failed:1',
|
||||
|
||||
// Runner retries Ic2
|
||||
'Ic2:instance:completed:2',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should resume workspace commands from per-workspace cursors', async () => {
|
||||
@@ -136,14 +133,17 @@ describe('UpgradeSequenceRunnerService — execution (integration)', () => {
|
||||
options: DEFAULT_OPTIONS,
|
||||
});
|
||||
|
||||
const wc2 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Wc2',
|
||||
workspaceId: WS_1,
|
||||
});
|
||||
const executed = await testGetExecutedMigrationsInOrder(context.dataSource);
|
||||
|
||||
expect(wc2).toEqual(
|
||||
expect.objectContaining({ name: 'Wc2', status: 'completed' }),
|
||||
);
|
||||
expect(executed.map(migrationRecordToKey)).toStrictEqual([
|
||||
// Seeds
|
||||
'Ic1:instance:completed:1',
|
||||
`Ic1:${WS_1}:completed:1`,
|
||||
`Wc1:${WS_1}:completed:1`,
|
||||
|
||||
// Runner resumes after Wc1, runs Wc2
|
||||
`Wc2:${WS_1}:completed:1`,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should enforce workspace sync barrier before instance step', async () => {
|
||||
@@ -162,13 +162,16 @@ describe('UpgradeSequenceRunnerService — execution (integration)', () => {
|
||||
options: DEFAULT_OPTIONS,
|
||||
});
|
||||
|
||||
const ic1 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Ic1',
|
||||
});
|
||||
const executed = await testGetExecutedMigrationsInOrder(context.dataSource);
|
||||
|
||||
expect(ic1).toEqual(
|
||||
expect.objectContaining({ name: 'Ic1', status: 'completed' }),
|
||||
);
|
||||
expect(executed.map(migrationRecordToKey)).toStrictEqual([
|
||||
// Seeds
|
||||
`Wc1:${WS_1}:completed:1`,
|
||||
|
||||
// Barrier passes, runner executes Ic1
|
||||
'Ic1:instance:completed:1',
|
||||
`Ic1:${WS_1}:completed:1`,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should skip data migration for slow instance commands when no workspaces exist', async () => {
|
||||
@@ -196,13 +199,15 @@ describe('UpgradeSequenceRunnerService — execution (integration)', () => {
|
||||
expect.objectContaining({ skipDataMigration: true }),
|
||||
);
|
||||
|
||||
const ic2 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Ic2',
|
||||
});
|
||||
const executed = await testGetExecutedMigrationsInOrder(context.dataSource);
|
||||
|
||||
expect(ic2).toEqual(
|
||||
expect.objectContaining({ name: 'Ic2', status: 'completed' }),
|
||||
);
|
||||
expect(executed.map(migrationRecordToKey)).toStrictEqual([
|
||||
// Seeds
|
||||
'Ic1:instance:completed:1',
|
||||
|
||||
// Runner runs Ic2 (slow, no workspaces → skip data migration)
|
||||
'Ic2:instance:completed:1',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should run data migration for slow instance commands when workspaces exist', async () => {
|
||||
@@ -233,13 +238,17 @@ describe('UpgradeSequenceRunnerService — execution (integration)', () => {
|
||||
expect.objectContaining({ skipDataMigration: false }),
|
||||
);
|
||||
|
||||
const ic1 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Ic1',
|
||||
});
|
||||
const executed = await testGetExecutedMigrationsInOrder(context.dataSource);
|
||||
|
||||
expect(ic1).toEqual(
|
||||
expect.objectContaining({ name: 'Ic1', status: 'completed' }),
|
||||
);
|
||||
expect(executed.map(migrationRecordToKey)).toStrictEqual([
|
||||
// Seeds
|
||||
'Ic0:instance:completed:1',
|
||||
`Ic0:${WS_1}:completed:1`,
|
||||
|
||||
// Runner runs Ic1 (slow, workspaces exist → run data migration)
|
||||
'Ic1:instance:completed:1',
|
||||
`Ic1:${WS_1}:completed:1`,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should run workspace commands for multiple workspaces successfully', async () => {
|
||||
@@ -277,21 +286,20 @@ describe('UpgradeSequenceRunnerService — execution (integration)', () => {
|
||||
|
||||
expect(report).toEqual({ totalSuccesses: 2, totalFailures: 0 });
|
||||
|
||||
const ws1Wc2 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Wc2',
|
||||
workspaceId: WS_1,
|
||||
});
|
||||
const ws2Wc2 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Wc2',
|
||||
workspaceId: WS_2,
|
||||
});
|
||||
const executed = await testGetExecutedMigrationsInOrder(context.dataSource);
|
||||
|
||||
expect(ws1Wc2).toEqual(
|
||||
expect.objectContaining({ name: 'Wc2', status: 'completed' }),
|
||||
);
|
||||
expect(ws2Wc2).toEqual(
|
||||
expect.objectContaining({ name: 'Wc2', status: 'completed' }),
|
||||
);
|
||||
expect(executed.map(migrationRecordToKey)).toStrictEqual([
|
||||
// Seeds
|
||||
'Ic1:instance:completed:1',
|
||||
`Ic1:${WS_1}:completed:1`,
|
||||
`Ic1:${WS_2}:completed:1`,
|
||||
`Wc1:${WS_1}:completed:1`,
|
||||
`Wc1:${WS_2}:completed:1`,
|
||||
|
||||
// Both workspaces run Wc2
|
||||
`Wc2:${WS_1}:completed:1`,
|
||||
`Wc2:${WS_2}:completed:1`,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should execute the full sequence from the initial cursor on a fresh run', async () => {
|
||||
@@ -320,20 +328,19 @@ describe('UpgradeSequenceRunnerService — execution (integration)', () => {
|
||||
options: DEFAULT_OPTIONS,
|
||||
});
|
||||
|
||||
const ic2 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Ic2',
|
||||
});
|
||||
const wc1 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Wc1',
|
||||
workspaceId: WS_1,
|
||||
});
|
||||
const executed = await testGetExecutedMigrationsInOrder(context.dataSource);
|
||||
|
||||
expect(ic2).toEqual(
|
||||
expect.objectContaining({ name: 'Ic2', status: 'completed' }),
|
||||
);
|
||||
expect(wc1).toEqual(
|
||||
expect.objectContaining({ name: 'Wc1', status: 'completed' }),
|
||||
);
|
||||
expect(executed.map(migrationRecordToKey)).toStrictEqual([
|
||||
// Seeds
|
||||
`Wc0:${WS_1}:completed:1`,
|
||||
'Ic1:instance:completed:1',
|
||||
`Ic1:${WS_1}:completed:1`,
|
||||
|
||||
// Runner resumes after Ic1, runs Ic2 then Wc1
|
||||
'Ic2:instance:completed:1',
|
||||
`Ic2:${WS_1}:completed:1`,
|
||||
`Wc1:${WS_1}:completed:1`,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should retry a failed workspace command', async () => {
|
||||
@@ -363,25 +370,18 @@ describe('UpgradeSequenceRunnerService — execution (integration)', () => {
|
||||
|
||||
expect(report.totalFailures).toBe(0);
|
||||
|
||||
const wc1 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Wc1',
|
||||
workspaceId: WS_1,
|
||||
});
|
||||
const wc2 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Wc2',
|
||||
workspaceId: WS_1,
|
||||
});
|
||||
const executed = await testGetExecutedMigrationsInOrder(context.dataSource);
|
||||
|
||||
expect(wc1).toEqual(
|
||||
expect.objectContaining({
|
||||
name: 'Wc1',
|
||||
status: 'completed',
|
||||
attempt: 2,
|
||||
}),
|
||||
);
|
||||
expect(wc2).toEqual(
|
||||
expect.objectContaining({ name: 'Wc2', status: 'completed' }),
|
||||
);
|
||||
expect(executed.map(migrationRecordToKey)).toStrictEqual([
|
||||
// Seeds
|
||||
'Ic1:instance:completed:1',
|
||||
`Ic1:${WS_1}:completed:1`,
|
||||
`Wc1:${WS_1}:failed:1`,
|
||||
|
||||
// Runner retries Wc1, then runs Wc2
|
||||
`Wc1:${WS_1}:completed:2`,
|
||||
`Wc2:${WS_1}:completed:1`,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should traverse a multi-segment sequence with sync barriers', async () => {
|
||||
@@ -412,20 +412,63 @@ describe('UpgradeSequenceRunnerService — execution (integration)', () => {
|
||||
|
||||
expect(report.totalFailures).toBe(0);
|
||||
|
||||
const ic2 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Ic2',
|
||||
});
|
||||
const wc2 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Wc2',
|
||||
workspaceId: WS_1,
|
||||
const executed = await testGetExecutedMigrationsInOrder(context.dataSource);
|
||||
|
||||
expect(executed.map(migrationRecordToKey)).toStrictEqual([
|
||||
// Seeds
|
||||
'Ic1:instance:completed:1',
|
||||
`Ic1:${WS_1}:completed:1`,
|
||||
`Wc1:${WS_1}:completed:1`,
|
||||
|
||||
// Sync barrier → Ic2, then Wc2
|
||||
'Ic2:instance:completed:1',
|
||||
`Ic2:${WS_1}:completed:1`,
|
||||
`Wc2:${WS_1}:completed:1`,
|
||||
]);
|
||||
});
|
||||
|
||||
it('should stop before the next instance step when running with workspace filter (-w)', async () => {
|
||||
const sequence = [
|
||||
makeFastInstance('Ic1'),
|
||||
makeWorkspace('Wc1'),
|
||||
makeWorkspace('Wc2'),
|
||||
makeFastInstance('Ic2'),
|
||||
makeWorkspace('Wc3'),
|
||||
];
|
||||
|
||||
setMockActiveWorkspaceIds([WS_1, WS_2]);
|
||||
|
||||
await seedInstanceMigration(context.dataSource, {
|
||||
name: 'Ic1',
|
||||
status: 'completed',
|
||||
workspaceIds: [WS_1, WS_2],
|
||||
});
|
||||
|
||||
expect(ic2).toEqual(
|
||||
expect.objectContaining({ name: 'Ic2', status: 'completed' }),
|
||||
);
|
||||
expect(wc2).toEqual(
|
||||
expect.objectContaining({ name: 'Wc2', status: 'completed' }),
|
||||
);
|
||||
const report = await context.runner.run({
|
||||
sequence,
|
||||
options: {
|
||||
...DEFAULT_OPTIONS,
|
||||
workspaceIds: [WS_1],
|
||||
},
|
||||
});
|
||||
|
||||
expect(report.totalFailures).toBe(0);
|
||||
expect(report.totalSuccesses).toBe(1);
|
||||
|
||||
const executed = await testGetExecutedMigrationsInOrder(context.dataSource);
|
||||
|
||||
expect(executed.map(migrationRecordToKey)).toStrictEqual([
|
||||
// Seeds
|
||||
'Ic1:instance:completed:1',
|
||||
`Ic1:${WS_1}:completed:1`,
|
||||
`Ic1:${WS_2}:completed:1`,
|
||||
|
||||
// Workspace segment: only WS_1 runs (filtered with -w)
|
||||
`Wc1:${WS_1}:completed:1`,
|
||||
`Wc2:${WS_1}:completed:1`,
|
||||
|
||||
// Ic2 and Wc3 never reached — runner stopped at instance step boundary
|
||||
]);
|
||||
});
|
||||
|
||||
it('should ignore migration records from inactive workspaces when resolving the global cursor', async () => {
|
||||
@@ -462,13 +505,17 @@ describe('UpgradeSequenceRunnerService — execution (integration)', () => {
|
||||
|
||||
expect(report.totalFailures).toBe(0);
|
||||
|
||||
const wc2 = await testGetLatestMigrationForCommand(context.dataSource, {
|
||||
name: 'Wc2',
|
||||
workspaceId: WS_1,
|
||||
});
|
||||
const executed = await testGetExecutedMigrationsInOrder(context.dataSource);
|
||||
|
||||
expect(wc2).toEqual(
|
||||
expect.objectContaining({ name: 'Wc2', status: 'completed' }),
|
||||
);
|
||||
expect(executed.map(migrationRecordToKey)).toStrictEqual([
|
||||
// Seeds
|
||||
'Ic1:instance:completed:1',
|
||||
`Ic1:${WS_1}:completed:1`,
|
||||
`Wc1:${WS_1}:completed:1`,
|
||||
`Wc2:${WS_2}:completed:1`,
|
||||
|
||||
// WS_2 is inactive, runner only processes WS_1
|
||||
`Wc2:${WS_1}:completed:1`,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
+3
-12
@@ -95,10 +95,7 @@ describe('UpgradeSequenceRunnerService — workspace segment alignment (integrat
|
||||
|
||||
const report = await context.runner.run({
|
||||
sequence,
|
||||
options: {
|
||||
...DEFAULT_OPTIONS,
|
||||
workspaceIds: [WS_1, WS_2, WS_3],
|
||||
},
|
||||
options: DEFAULT_OPTIONS,
|
||||
});
|
||||
|
||||
expect(report.totalFailures).toBe(0);
|
||||
@@ -504,10 +501,7 @@ describe('UpgradeSequenceRunnerService — workspace segment alignment (integrat
|
||||
await expect(
|
||||
context.runner.run({
|
||||
sequence,
|
||||
options: {
|
||||
...DEFAULT_OPTIONS,
|
||||
workspaceIds: [WS_1, WS_2],
|
||||
},
|
||||
options: DEFAULT_OPTIONS,
|
||||
}),
|
||||
).rejects.toThrow('Ic1 temporary failure');
|
||||
|
||||
@@ -542,10 +536,7 @@ describe('UpgradeSequenceRunnerService — workspace segment alignment (integrat
|
||||
// Second run — Ic1 retries and succeeds, then WC segment runs for all 3
|
||||
const report = await context.runner.run({
|
||||
sequence,
|
||||
options: {
|
||||
...DEFAULT_OPTIONS,
|
||||
workspaceIds: [WS_1, WS_2, WS_3],
|
||||
},
|
||||
options: DEFAULT_OPTIONS,
|
||||
});
|
||||
|
||||
expect(report.totalFailures).toBe(0);
|
||||
|
||||
-39
@@ -296,45 +296,6 @@ export const seedWorkspaceMigration = async (
|
||||
}
|
||||
};
|
||||
|
||||
export const testCountMigrationsForCommand = async (
|
||||
dataSource: DataSource,
|
||||
{
|
||||
name,
|
||||
workspaceId = null,
|
||||
}: {
|
||||
name: string;
|
||||
workspaceId?: string | null;
|
||||
},
|
||||
): Promise<number> => {
|
||||
const rows = await dataSource.query(
|
||||
`SELECT COUNT(*)::int AS count FROM core."upgradeMigration"
|
||||
WHERE name = $1 AND ($2::uuid IS NULL AND "workspaceId" IS NULL OR "workspaceId" = $2)`,
|
||||
[name, workspaceId],
|
||||
);
|
||||
|
||||
return rows[0].count;
|
||||
};
|
||||
|
||||
export const testGetLatestMigrationForCommand = async (
|
||||
dataSource: DataSource,
|
||||
{
|
||||
name,
|
||||
workspaceId = null,
|
||||
}: {
|
||||
name: string;
|
||||
workspaceId?: string | null;
|
||||
},
|
||||
): Promise<{ name: string; status: string; attempt: number } | null> => {
|
||||
const rows = await dataSource.query(
|
||||
`SELECT name, status, attempt FROM core."upgradeMigration"
|
||||
WHERE name = $1 AND ($2::uuid IS NULL AND "workspaceId" IS NULL OR "workspaceId" = $2)
|
||||
ORDER BY attempt DESC LIMIT 1`,
|
||||
[name, workspaceId],
|
||||
);
|
||||
|
||||
return rows.length > 0 ? rows[0] : null;
|
||||
};
|
||||
|
||||
export type ExecutedMigrationRecord = {
|
||||
name: string;
|
||||
status: string;
|
||||
|
||||
Reference in New Issue
Block a user