Keep artifact list and beta queries free of file bytes
This commit is contained in:
@@ -154,7 +154,7 @@ describe('beta revisions', () => {
|
||||
it.each(['stale', 'identical', 'empty', 'unbundled', 'duplicate', 'closed', 'deleted', 'interrupted', 'activating'])('rejects %s updates without saving a revision', async reason => {
|
||||
const x = await setup();
|
||||
const upload = { scriptId: x.script.id, content: Buffer.from('changed') };
|
||||
if (reason === 'identical') upload.content = Buffer.from(x.version.content);
|
||||
if (reason === 'identical') upload.content = Buffer.from((await import('../../src/lib/server/fleet/scripts.js')).getVersionContent(x.version.id));
|
||||
if (reason === 'empty') upload.content = Buffer.alloc(0);
|
||||
if (reason === 'unbundled') upload.scriptId = 999;
|
||||
if (reason === 'closed') x.db.prepare("UPDATE betas SET state = 'promoted' WHERE id = ?").run(x.betaId);
|
||||
|
||||
@@ -35,7 +35,7 @@ it('covers every foreign key child prefix, including lazy host and library schem
|
||||
const prefixes = indexes.filter(index => !index.partial).map(index =>
|
||||
(handle.pragma(`index_info(${index.name})`) as { name: string }[])[0]?.name);
|
||||
const columns = handle.pragma(`table_info(${name})`) as { name: string; type: string; pk: number }[];
|
||||
if (columns.filter(column => column.pk).length === 1) prefixes.push(columns.find(column => column.pk)?.name);
|
||||
if (columns.filter(column => column.pk).length === 1) prefixes.push(columns.find(column => column.pk)!.name);
|
||||
for (const key of foreignKeys.filter(key => key.seq === 0)) {
|
||||
if (!prefixes.includes(key.from)) missing.push(`${name}.${key.from}`);
|
||||
}
|
||||
|
||||
@@ -378,7 +378,7 @@ describe('drift rebuild campaigns', () => {
|
||||
expect(await readFile(stray, 'utf8')).toBe('untracked');
|
||||
expect(db().prepare('SELECT rel_path, verb, content_before, content_after FROM deployment_files WHERE deployment_id = ? ORDER BY rel_path').all(campaignId))
|
||||
.toEqual([
|
||||
{ rel_path: 'managed.gsc', verb: 'updated', content_before: Buffer.from('old content'), content_after: version.content },
|
||||
{ rel_path: 'managed.gsc', verb: 'updated', content_before: Buffer.from('old content'), content_after: (await import('../../src/lib/server/fleet/scripts.js')).getVersionContent(version.id) },
|
||||
{ rel_path: 'stray.cfg', verb: 'removed', content_before: Buffer.from('untracked'), content_after: null }
|
||||
]);
|
||||
await sweep();
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
import Database from 'better-sqlite3';
|
||||
import { afterEach, beforeEach, expect, it, vi } from 'vitest';
|
||||
import { newFixture, type Fixture } from '../helpers/fixture.js';
|
||||
import { migrateVersionLayout } from '../../src/lib/server/db/version-layout.js';
|
||||
|
||||
let fixture: Fixture;
|
||||
beforeEach(async () => { vi.resetModules(); fixture = await newFixture([]); });
|
||||
afterEach(async () => { vi.restoreAllMocks(); await fixture.cleanup(); });
|
||||
|
||||
it('returns metadata without plugin bytes and lists many artifacts in two queries', async () => {
|
||||
const { createScript, getVersion, getVersionContent, getLatestVersion, listVersions } = await import('../../src/lib/server/fleet/scripts.js');
|
||||
const { scriptCatalog, betaScriptMetadata } = await import('../../src/lib/server/fleet/library-metadata.js');
|
||||
const { db } = await import('../../src/lib/server/db/index.js');
|
||||
const content = Buffer.alloc(1024 * 1024, 77);
|
||||
let id = 0;
|
||||
for (let i = 0; i < 20; i++) id = createScript({ name: `plugin-${i}`, kind: 'plugin', targetRelPath: 'plugin.dll', content }).version.id;
|
||||
expect(getVersionContent(id)).toEqual(content);
|
||||
expect(getVersion(id)).not.toHaveProperty('content');
|
||||
expect(getLatestVersion(getVersion(id)!.script_id)).not.toHaveProperty('content');
|
||||
expect(listVersions(getVersion(id)!.script_id)[0]).not.toHaveProperty('content');
|
||||
const prepare = vi.spyOn(db(), 'prepare');
|
||||
expect(scriptCatalog('plugin')).toHaveLength(20);
|
||||
expect(prepare).toHaveBeenCalledTimes(2);
|
||||
betaScriptMetadata([]);
|
||||
expect(prepare.mock.calls.every(([sql]) => !/\bcontent\b/.test(sql))).toBe(true);
|
||||
});
|
||||
|
||||
it('moves legacy content last without losing bytes, IDs, indexes, triggers or references', () => {
|
||||
const handle = new Database(':memory:');
|
||||
try {
|
||||
handle.exec(`PRAGMA foreign_keys=ON;
|
||||
CREATE TABLE script_versions(id INTEGER PRIMARY KEY AUTOINCREMENT, script_id INTEGER,
|
||||
version INTEGER, content BLOB NOT NULL, state TEXT, release_version TEXT, UNIQUE(script_id,version));
|
||||
CREATE INDEX version_state ON script_versions(state);
|
||||
CREATE TABLE child(version INTEGER REFERENCES script_versions(id));
|
||||
CREATE TABLE updates(n INTEGER); INSERT INTO updates VALUES(0);
|
||||
CREATE TRIGGER version_update AFTER UPDATE ON script_versions BEGIN UPDATE updates SET n=n+1; END;
|
||||
INSERT INTO script_versions VALUES(7,1,1,x'00ff','tested','v1');
|
||||
INSERT INTO child VALUES(7);
|
||||
INSERT INTO script_versions VALUES(100,1,2,x'aa','untested',NULL);
|
||||
DELETE FROM script_versions WHERE id=100;`);
|
||||
const before = handle.prepare('SELECT * FROM script_versions').all();
|
||||
migrateVersionLayout(handle);
|
||||
migrateVersionLayout(handle);
|
||||
expect(handle.prepare('SELECT * FROM script_versions').all()).toEqual(before);
|
||||
expect((handle.pragma('table_info(script_versions)') as { name: string }[]).at(-1)?.name).toBe('content');
|
||||
expect(handle.pragma('foreign_key_check')).toEqual([]);
|
||||
expect(handle.prepare("INSERT INTO script_versions(script_id,version,content) VALUES(1,3,x'01')").run().lastInsertRowid).toBe(101);
|
||||
handle.prepare("UPDATE script_versions SET state='tested' WHERE id=101").run();
|
||||
expect(handle.prepare('SELECT n FROM updates').get()).toEqual({ n: 1 });
|
||||
expect((handle.pragma('index_list(script_versions)') as { name: string }[]).some(index => index.name === 'version_state')).toBe(true);
|
||||
} finally { handle.close(); }
|
||||
});
|
||||
@@ -40,7 +40,7 @@ async function setup(capable = true) {
|
||||
const baseline = promoted('baseline', 'Baseline', Buffer.from('set sv_voice "1"\n'));
|
||||
for (const [root, artifact] of [[scriptRoot, script], [pluginRoot, plugin]] as const) {
|
||||
const { version } = artifact;
|
||||
contents.set(`${root}/${artifact.script.target_rel_path}`, version.content);
|
||||
contents.set(`${root}/${artifact.script.target_rel_path}`, (await import('../../src/lib/server/fleet/scripts.js')).getVersionContent(version.id));
|
||||
recordScript(server, artifact.script.target_rel_path, version.id, version.sha256, version.size, 'test');
|
||||
}
|
||||
const { getPromotedServerConfig, getServerMapConfig, MAP_CONFIG_FILE } = await import('../../src/lib/server/fleet/server-config.js');
|
||||
|
||||
@@ -26,7 +26,7 @@ describe('plugin release versions', () => {
|
||||
[3, 'v3.0.4'], [2, 'v3.0.4'], [1, 'v3.1.0']
|
||||
]);
|
||||
expect(older.sha256).not.toBe(rebuild.sha256);
|
||||
expect(getVersion(version.id)!.content).toEqual(plugin.content);
|
||||
expect((await import('../../src/lib/server/fleet/scripts.js')).getVersionContent(version.id)).toEqual(plugin.content);
|
||||
});
|
||||
|
||||
it('allows unlabeled uploads and non-semver release tags', async () => {
|
||||
@@ -53,8 +53,9 @@ describe('plugin release versions', () => {
|
||||
db().exec('ALTER TABLE script_versions DROP COLUMN release_version');
|
||||
closeDb();
|
||||
expect(getVersion(version.id)).toMatchObject({
|
||||
id: version.id, version: 1, release_version: null, sha256: version.sha256, content: plugin.content
|
||||
id: version.id, version: 1, release_version: null, sha256: version.sha256
|
||||
});
|
||||
expect((await import('../../src/lib/server/fleet/scripts.js')).getVersionContent(version.id)).toEqual(plugin.content);
|
||||
closeDb();
|
||||
expect(getVersion(version.id)!.release_version).toBeNull();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user