Fix release version labels across deployment views
This commit is contained in:
@@ -174,7 +174,7 @@ async function moveTarget() {
|
||||
}
|
||||
|
||||
describe('rebalance instance migrations', () => {
|
||||
it('preserves deployed configuration, patched file versions, identity and credentials on an active source', async () => {
|
||||
it.each(['current', 'legacy'])('preserves deployed files (%s)', async (recipeFormat) => {
|
||||
const { config } = await import('../../src/lib/server/config.js');
|
||||
Object.assign(config.rcon, { password: '' });
|
||||
const { createScript, uploadNewVersion } =
|
||||
@@ -196,7 +196,16 @@ describe('rebalance instance migrations', () => {
|
||||
scopeValue: 't5',
|
||||
content: Buffer.from('original deployed script')
|
||||
});
|
||||
for (const artifact of [baseline, script])
|
||||
const plugin = createScript({
|
||||
releaseVersion: 'v2.4.0',
|
||||
name: 'Deployed plugin',
|
||||
kind: 'plugin',
|
||||
targetRelPath: 'preserved.dll',
|
||||
scopeType: 'game',
|
||||
scopeValue: 't5',
|
||||
content: Buffer.from('deployed plugin')
|
||||
});
|
||||
for (const artifact of [baseline, script, plugin])
|
||||
db()
|
||||
.prepare('UPDATE scripts SET current_version_id=? WHERE id=?')
|
||||
.run(artifact.version.id, artifact.script.id);
|
||||
@@ -243,10 +252,36 @@ describe('rebalance instance migrations', () => {
|
||||
const original = nativeInstance(job.name)!;
|
||||
const recipe = captureMoveRecipe(job.name);
|
||||
expect(recipe.files.every((file) => file.content === undefined)).toBe(true);
|
||||
expect(recipe.files.find((file) => file.path === 'preserved.gsc')).toMatchObject({
|
||||
versionId: patched.id,
|
||||
version: patched.version,
|
||||
releaseVersion: '1.0.1'
|
||||
});
|
||||
expect(recipe.files.find((file) => file.path === 'preserved.dll')).toMatchObject({
|
||||
versionId: plugin.version.id,
|
||||
releaseVersion: 'v2.4.0'
|
||||
});
|
||||
const id = requestInstanceMove(job.name, target.id, {
|
||||
triggeredBy: 'rebalance:test',
|
||||
recipe
|
||||
});
|
||||
if (recipeFormat === 'legacy') {
|
||||
const stored = db()
|
||||
.prepare(
|
||||
'SELECT recipe, recipe_summary FROM server_launch_recipes WHERE deployment_id=?'
|
||||
)
|
||||
.get(id) as { recipe: string; recipe_summary: string };
|
||||
const legacy = (encoded: string) => {
|
||||
const value = JSON.parse(encoded);
|
||||
for (const file of value.files) delete file.releaseVersion;
|
||||
return JSON.stringify(value);
|
||||
};
|
||||
db()
|
||||
.prepare(
|
||||
'UPDATE server_launch_recipes SET recipe=?,recipe_summary=? WHERE deployment_id=?'
|
||||
)
|
||||
.run(legacy(stored.recipe), legacy(stored.recipe_summary), id);
|
||||
}
|
||||
await advance();
|
||||
const removal = hostMigration(id)!.remove_operation_id!;
|
||||
expect(removal).toBeTruthy();
|
||||
@@ -304,7 +339,14 @@ describe('rebalance instance migrations', () => {
|
||||
generation: 2
|
||||
});
|
||||
expect(getHost(hostId)?.mode).toBe('active');
|
||||
expect(JSON.stringify(getDeployment(id)?.recipe)).not.toContain(launch.rconPassword);
|
||||
const displayedRecipe = getDeployment(id)?.recipe;
|
||||
expect(displayedRecipe?.files.find((file) => file.path === 'preserved.gsc')).toMatchObject({
|
||||
releaseVersion: '1.0.1'
|
||||
});
|
||||
expect(displayedRecipe?.files.find((file) => file.path === 'preserved.dll')).toMatchObject({
|
||||
releaseVersion: 'v2.4.0'
|
||||
});
|
||||
expect(JSON.stringify(displayedRecipe)).not.toContain(launch.rconPassword);
|
||||
});
|
||||
|
||||
it('rechecks occupancy, target capacity and run admission before removing the source', async () => {
|
||||
@@ -1773,17 +1815,21 @@ describe('native instance provisioning', () => {
|
||||
expect(getHost(hostId)?.mode).toBe('active');
|
||||
});
|
||||
it('builds campaign replacements with requested plugins, configuration, additions and removals', async () => {
|
||||
const { createScript } = await import('../../src/lib/server/fleet/scripts.js');
|
||||
const { createScript, uploadNewVersion } =
|
||||
await import('../../src/lib/server/fleet/scripts.js');
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
let replacementPluginId = 0;
|
||||
for (const path of ['replace.dll', 'remove.dll']) {
|
||||
const file = createScript({
|
||||
name: path,
|
||||
kind: 'plugin',
|
||||
releaseVersion: 'v2.0.0',
|
||||
targetRelPath: path,
|
||||
scopeType: 'game',
|
||||
scopeValue: 't5',
|
||||
content: Buffer.from('promoted plugin')
|
||||
});
|
||||
if (path === 'replace.dll') replacementPluginId = file.script.id;
|
||||
db()
|
||||
.prepare('UPDATE scripts SET current_version_id=? WHERE id=?')
|
||||
.run(file.version.id, file.script.id);
|
||||
@@ -1791,6 +1837,21 @@ describe('native instance provisioning', () => {
|
||||
const job = await queue();
|
||||
await advance();
|
||||
await acknowledge(job.deploymentId);
|
||||
const replacement = uploadNewVersion(
|
||||
replacementPluginId,
|
||||
Buffer.from('requested plugin'),
|
||||
undefined,
|
||||
'v2.1.0'
|
||||
);
|
||||
const addition = createScript({
|
||||
name: 'New script',
|
||||
kind: 'script',
|
||||
releaseVersion: '3.2.1',
|
||||
targetRelPath: 'new.gsc',
|
||||
scopeType: 'game',
|
||||
scopeValue: 't5',
|
||||
content: Buffer.from('new script')
|
||||
});
|
||||
db()
|
||||
.prepare(
|
||||
"INSERT INTO deployments(id,action,started_at) VALUES('files-rebuild','promote',?)"
|
||||
@@ -1803,9 +1864,19 @@ describe('native instance provisioning', () => {
|
||||
.run(job.name);
|
||||
const { queueFileChange } = await import('../../src/lib/server/fleet/staged-files.js');
|
||||
const changes = [
|
||||
{ kind: 'plugin', relPath: 'replace.dll', content: Buffer.from('requested plugin') },
|
||||
{
|
||||
kind: 'plugin',
|
||||
relPath: 'replace.dll',
|
||||
content: Buffer.from('requested plugin'),
|
||||
scriptVersionId: replacement.id
|
||||
},
|
||||
{ kind: 'plugin', relPath: 'remove.dll', content: null },
|
||||
{ kind: 'script', relPath: 'new.gsc', content: Buffer.from('new script') },
|
||||
{
|
||||
kind: 'script',
|
||||
relPath: 'new.gsc',
|
||||
content: Buffer.from('new script'),
|
||||
scriptVersionId: addition.version.id
|
||||
},
|
||||
{ kind: 'config', relPath: 'custom.cfg', content: Buffer.from('set custom 1') }
|
||||
] as const;
|
||||
for (const change of changes)
|
||||
@@ -1839,6 +1910,21 @@ describe('native instance provisioning', () => {
|
||||
if (change.content === null) expect(actual).toBeUndefined();
|
||||
else expect(Buffer.from(actual!.content!, 'base64')).toEqual(change.content);
|
||||
}
|
||||
expect(recipe.files.find((file) => file.path === 'replace.dll')).toMatchObject({
|
||||
versionId: replacement.id,
|
||||
version: replacement.version,
|
||||
releaseVersion: 'v2.1.0'
|
||||
});
|
||||
expect(recipe.files.find((file) => file.path === 'new.gsc')).toMatchObject({
|
||||
versionId: addition.version.id,
|
||||
version: addition.version.version,
|
||||
releaseVersion: '3.2.1'
|
||||
});
|
||||
expect(recipe.files.find((file) => file.path === 'custom.cfg')).toMatchObject({
|
||||
versionId: null,
|
||||
version: null,
|
||||
releaseVersion: null
|
||||
});
|
||||
const files = getHostOperation(staged.stage_operation_id)!.payload.files as Array<{
|
||||
id: number;
|
||||
path: string;
|
||||
|
||||
Reference in New Issue
Block a user