From e12176c79dc463e2b61adeb418b129dcb733fe22 Mon Sep 17 00:00:00 2001 From: Garrett Stevens Date: Fri, 12 Dec 2025 21:12:53 +0000 Subject: [PATCH 1/4] Use env var instead of flag in CLI tests --- packages/apollo-cli/src/test/test.ts | 701 ++++++++++++-------------- packages/apollo-cli/src/test/utils.ts | 16 +- 2 files changed, 339 insertions(+), 378 deletions(-) diff --git a/packages/apollo-cli/src/test/test.ts b/packages/apollo-cli/src/test/test.ts index 2302f1b95..ee62d1aee 100644 --- a/packages/apollo-cli/src/test/test.ts +++ b/packages/apollo-cli/src/test/test.ts @@ -32,7 +32,6 @@ import { MongoClient } from 'mongodb' import { Shell, deleteAllChecks } from './utils.js' const apollo = 'yarn dev' -const P = '--profile testAdmin' // let client = MongoClient let client: MongoClient let configFile = '' @@ -50,10 +49,10 @@ void describe('Test CLI', () => { `Backup config file ${configFileBak} already exists. If safe to do so, delete it before testing`, ) } - new Shell(`${apollo} config ${P} address http://localhost:3999`) - new Shell(`${apollo} config ${P} accessType root`) - new Shell(`${apollo} config ${P} rootPassword pass`) - new Shell(`${apollo} login ${P} -f`) + new Shell(`${apollo} config address http://localhost:3999`) + new Shell(`${apollo} config accessType root`) + new Shell(`${apollo} config rootPassword pass`) + new Shell(`${apollo} login -f`) }) after(async () => { @@ -95,95 +94,95 @@ void describe('Test CLI', () => { }) void globalThis.itName('Config invalid keys', () => { - let p = new Shell(`${apollo} config ${P} address spam`, false) + let p = new Shell(`${apollo} config address spam`, false) assert.strictEqual(1, p.returncode) assert.ok(p.stderr.includes('Invalid setting:')) - p = new Shell(`${apollo} config ${P} ADDRESS http://localhost:3999`, false) + p = new Shell(`${apollo} config ADDRESS http://localhost:3999`, false) assert.strictEqual(1, p.returncode) assert.ok(p.stderr.includes('Invalid setting:')) - p = new Shell(`${apollo} config ${P} accessType spam`, false) + p = new Shell(`${apollo} config accessType spam`, false) assert.strictEqual(1, p.returncode) assert.ok(p.stderr.includes('Invalid setting:')) }) void globalThis.itName('Can change access type', () => { - const p = new Shell(`${apollo} config ${P} accessType google`) + const p = new Shell(`${apollo} config accessType google`) assert.strictEqual('', p.stdout.trim()) }) void globalThis.itName('Apollo status', () => { - let p = new Shell(`${apollo} status ${P}`) + let p = new Shell(`${apollo} status`) assert.strictEqual(p.stdout.trim(), 'testAdmin: Logged in') - new Shell(`${apollo} logout ${P}`) - p = new Shell(`${apollo} status ${P}`) + new Shell(`${apollo} logout`) + p = new Shell(`${apollo} status`) assert.strictEqual(p.stdout.trim(), 'testAdmin: Logged out') - new Shell(`${apollo} login ${P} -f`) + new Shell(`${apollo} login -f`) }) void globalThis.itName('Feature get', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, ) new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv2 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv2 -f`, ) - let p = new Shell(`${apollo} feature get ${P} -a vv1`) + let p = new Shell(`${apollo} feature get -a vv1`) assert.ok(p.stdout.includes('ctgA')) assert.ok(p.stdout.includes('SomeContig')) - p = new Shell(`${apollo} feature get ${P} -r ctgA`, false) + p = new Shell(`${apollo} feature get -r ctgA`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('found in more than one assembly')) - p = new Shell(`${apollo} feature get ${P} -a vv1 -r ctgA`) + p = new Shell(`${apollo} feature get -a vv1 -r ctgA`) let out = JSON.parse(p.stdout) assert.ok(Object.keys(out.at(0)).length > 2) - p = new Shell(`${apollo} feature get ${P} -a vv1 -r ctgA -s 40 -e 41`) + p = new Shell(`${apollo} feature get -a vv1 -r ctgA -s 40 -e 41`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) - p = new Shell(`${apollo} feature get ${P} -a vv1 -r ctgA -s 1000 -e 1000`) + p = new Shell(`${apollo} feature get -a vv1 -r ctgA -s 1000 -e 1000`) out = JSON.parse(p.stdout) assert.deepStrictEqual(out, []) - p = new Shell(`${apollo} feature get ${P} -r FOOBAR`) + p = new Shell(`${apollo} feature get -r FOOBAR`) out = JSON.parse(p.stdout) assert.deepStrictEqual(out, []) - p = new Shell(`${apollo} feature get ${P} -a FOOBAR -r ctgA`, false) + p = new Shell(`${apollo} feature get -a FOOBAR -r ctgA`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('returned 0 assemblies')) }) void globalThis.itName('Assembly get', () => { new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a vv1 -e -f`, + `${apollo} assembly add-from-fasta test_data/tiny.fasta -a vv1 -e -f`, ) new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a vv2 -e -f`, + `${apollo} assembly add-from-fasta test_data/tiny.fasta -a vv2 -e -f`, ) new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a vv3 -e -f`, + `${apollo} assembly add-from-fasta test_data/tiny.fasta -a vv3 -e -f`, ) - let p = new Shell(`${apollo} assembly get ${P}`) + let p = new Shell(`${apollo} assembly get`) assert.ok(p.stdout.includes('vv1')) assert.ok(p.stdout.includes('vv2')) assert.ok(p.stdout.includes('vv3')) - p = new Shell(`${apollo} assembly get ${P} -a vv1 vv2`) + p = new Shell(`${apollo} assembly get -a vv1 vv2`) assert.ok(p.stdout.includes('vv1')) assert.ok(p.stdout.includes('vv2')) assert.ok(p.stdout.includes('vv3') == false) const out = JSON.parse(p.stdout) const aid = out.find((x: any) => x.name === 'vv1')._id - p = new Shell(`${apollo} assembly get ${P} -a ${aid} vv2`) + p = new Shell(`${apollo} assembly get -a ${aid} vv2`) assert.ok(p.stdout.includes('vv1')) assert.ok(p.stdout.includes('vv2')) assert.ok(p.stdout.includes('vv3') == false) @@ -191,26 +190,26 @@ void describe('Test CLI', () => { void globalThis.itName('Delete assembly', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a volvox1 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a volvox1 -f`, ) new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a volvox2 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a volvox2 -f`, ) new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a volvox3 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a volvox3 -f`, ) let p = new Shell( - `${apollo} assembly get ${P} | jq '.[] | select(.name == "volvox1") | ._id'`, + `${apollo} assembly get | jq '.[] | select(.name == "volvox1") | ._id'`, ) const aid = p.stdout.trim() - p = new Shell(`${apollo} assembly delete ${P} -v -a ${aid} volvox2 volvox2`) + p = new Shell(`${apollo} assembly delete -v -a ${aid} volvox2 volvox2`) const out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) assert.ok(p.stderr.includes('2 ')) - new Shell(`${apollo} assembly delete ${P} -a ${aid} volvox2`) - p = new Shell(`${apollo} assembly get ${P}`) + new Shell(`${apollo} assembly delete -a ${aid} volvox2`) + p = new Shell(`${apollo} assembly get`) assert.ok(p.stdout.includes(aid) == false) assert.ok(p.stdout.includes('volvox1') == false) assert.ok(p.stdout.includes('volvox2') == false) @@ -219,47 +218,45 @@ void describe('Test CLI', () => { void globalThis.itName('Id reader', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a v1 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a v1 -f`, ) new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a v2 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a v2 -f`, ) new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a v3 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a v3 -f`, ) - let p = new Shell(`${apollo} assembly get ${P}`) + let p = new Shell(`${apollo} assembly get`) const xall = JSON.parse(p.stdout) - p = new Shell(`${apollo} assembly get ${P} -a v1 v2`) + p = new Shell(`${apollo} assembly get -a v1 v2`) let out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) // This is interpreted as an assembly named 'v1 v2' - p = new Shell(`echo v1 v2 | ${apollo} assembly get ${P} -a -`) + p = new Shell(`echo v1 v2 | ${apollo} assembly get -a -`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 0) // These are two assemblies - p = new Shell(`echo -e 'v1 \n v2' | ${apollo} assembly get ${P} -a -`) + p = new Shell(`echo -e 'v1 \n v2' | ${apollo} assembly get -a -`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) - p = new Shell( - `${apollo} assembly get ${P} | ${apollo} assembly get ${P} -a -`, - ) + p = new Shell(`${apollo} assembly get | ${apollo} assembly get -a -`) out = JSON.parse(p.stdout) assert.ok(out.length >= 3) // From json file - new Shell(`${apollo} assembly get ${P} > test_data/tmp.json`) - p = new Shell(`${apollo} assembly get ${P} -a test_data/tmp.json`) + new Shell(`${apollo} assembly get > test_data/tmp.json`) + p = new Shell(`${apollo} assembly get -a test_data/tmp.json`) out = JSON.parse(p.stdout) assert.ok(out.length >= 3) fs.unlinkSync('test_data/tmp.json') // From text file, one name or id per line fs.writeFileSync('test_data/tmp.txt', 'v1 \n v2 \r\n v3 \n') - p = new Shell(`${apollo} assembly get ${P} -a test_data/tmp.txt`) + p = new Shell(`${apollo} assembly get -a test_data/tmp.txt`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 3) fs.unlinkSync('test_data/tmp.txt') @@ -267,7 +264,7 @@ void describe('Test CLI', () => { // From json string const aid = xall.at(0)._id let j = `{"_id": "${aid}"}` - p = new Shell(`${apollo} assembly get ${P} -a '${j}'`) + p = new Shell(`${apollo} assembly get -a '${j}'`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) assert.strictEqual(out.at(0)._id, aid) @@ -275,54 +272,52 @@ void describe('Test CLI', () => { const id1 = xall.at(0)._id const id2 = xall.at(1)._id j = `[{"_id": "${id1}"}, {"_id": "${id2}"}]` - p = new Shell(`${apollo} assembly get ${P} -a '${j}'`) + p = new Shell(`${apollo} assembly get -a '${j}'`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) j = `{"XYZ": "${aid}"}` - p = new Shell(`${apollo} assembly get ${P} -a '${j}'`) + p = new Shell(`${apollo} assembly get -a '${j}'`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 0) - p = new Shell(`${apollo} assembly get ${P} -a '[...'`) + p = new Shell(`${apollo} assembly get -a '[...'`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 0) }) void globalThis.itName('Add assembly from gff', () => { let p = new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 --omit-features -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 --omit-features -f`, ) const out = JSON.parse(p.stdout) assert.ok(Object.keys(out.fileIds).includes('fa')) // Get id of assembly named vv1 and check there are no features - p = new Shell(`${apollo} assembly get ${P} -a vv1`) + p = new Shell(`${apollo} assembly get -a vv1`) assert.ok(p.stdout.includes('vv1')) assert.ok(p.stdout.includes('vv2') == false) const asm_id = JSON.parse(p.stdout).at(0)._id - p = new Shell(`${apollo} refseq get ${P}`) + p = new Shell(`${apollo} refseq get`) const refseq = JSON.parse(p.stdout.trim()) const vv1ref = refseq.filter((x: any) => x.assembly === asm_id) const refseq_id = vv1ref.find((x: any) => x.name === 'ctgA')._id - p = new Shell(`${apollo} feature get ${P} -r ${refseq_id}`) + p = new Shell(`${apollo} feature get -r ${refseq_id}`) const ff = JSON.parse(p.stdout) assert.deepStrictEqual(ff, []) p = new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1`, false, ) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('Error: Assembly "vv1" already exists')) // Default assembly name - new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -f`, - ) - p = new Shell(`${apollo} assembly get ${P} -a tiny.fasta.gff3`) + new Shell(`${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -f`) + p = new Shell(`${apollo} assembly get -a tiny.fasta.gff3`) assert.ok(p.stdout.includes('tiny.fasta.gff3')) }) @@ -335,18 +330,18 @@ void describe('Test CLI', () => { stream.close() new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tmp.fa -a test -e -f`, + `${apollo} assembly add-from-fasta test_data/tmp.fa -a test -e -f`, true, 60_000, ) new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tmp.fa -a test -f`, + `${apollo} assembly add-from-gff test_data/tmp.fa -a test -f`, false, 60_000, ) new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tmp.fa -a test -e -f`, + `${apollo} assembly add-from-fasta test_data/tmp.fa -a test -e -f`, true, 60_000, ) @@ -355,11 +350,11 @@ void describe('Test CLI', () => { }) void globalThis.itName('Checks are triggered and resolved', () => { - new Shell(`${apollo} assembly add-from-gff ${P} test_data/checks.gff -f`) - let p = new Shell(`${apollo} feature get ${P} -a checks.gff`) + new Shell(`${apollo} assembly add-from-gff test_data/checks.gff -f`) + let p = new Shell(`${apollo} feature get -a checks.gff`) const out = JSON.parse(p.stdout) - p = new Shell(`${apollo} feature check ${P} -a checks.gff`) + p = new Shell(`${apollo} feature check -a checks.gff`) assert.deepStrictEqual(p.stdout.trim(), '[]') // No failing check // Get the ID of the CDS. We need need it to modify the CDS coordinates @@ -374,29 +369,25 @@ void describe('Test CLI', () => { const cds_id = cds._id // Introduce problems - new Shell( - `${apollo} feature edit-coords ${P} -i ${cds_id} --start 4 --end 24`, - ) - p = new Shell(`${apollo} feature check ${P} -a checks.gff`) + new Shell(`${apollo} feature edit-coords -i ${cds_id} --start 4 --end 24`) + p = new Shell(`${apollo} feature check -a checks.gff`) const checks = JSON.parse(p.stdout) assert.strictEqual(checks.length, 2) assert.ok(p.stdout.includes('InternalStopCodon')) assert.ok(p.stdout.includes('MissingStopCodon')) // Problems fixed - new Shell( - `${apollo} feature edit-coords ${P} -i ${cds_id} --start 16 --end 27`, - ) - p = new Shell(`${apollo} feature check ${P} -a checks.gff`) + new Shell(`${apollo} feature edit-coords -i ${cds_id} --start 16 --end 27`) + p = new Shell(`${apollo} feature check -a checks.gff`) assert.deepStrictEqual(JSON.parse(p.stdout).length, 0) }) void globalThis.itName('FIXME: Checks stay after invalid operation', () => { - new Shell(`${apollo} assembly add-from-gff ${P} test_data/checks.gff -f`) - let p = new Shell(`${apollo} feature get ${P} -a checks.gff`) + new Shell(`${apollo} assembly add-from-gff test_data/checks.gff -f`) + let p = new Shell(`${apollo} feature get -a checks.gff`) const out = JSON.parse(p.stdout) - p = new Shell(`${apollo} feature check ${P} -a checks.gff`) + p = new Shell(`${apollo} feature check -a checks.gff`) assert.deepStrictEqual(p.stdout.trim(), '[]') // No failing check // Get the ID of the CDS. We need need it to modify the CDS coordinates @@ -411,25 +402,20 @@ void describe('Test CLI', () => { const cds_id = cds._id // Introduce problems - new Shell( - `${apollo} feature edit-coords ${P} -i ${cds_id} --start 4 --end 24`, - ) - p = new Shell(`${apollo} feature check ${P} -a checks.gff`) + new Shell(`${apollo} feature edit-coords -i ${cds_id} --start 4 --end 24`) + p = new Shell(`${apollo} feature check -a checks.gff`) let checks = JSON.parse(p.stdout) assert.strictEqual(checks.length, 2) assert.ok(p.stdout.includes('InternalStopCodon')) assert.ok(p.stdout.includes('MissingStopCodon')) // Do something invalid: extend CDS beyond parent - p = new Shell( - `${apollo} feature edit-coords ${P} -i ${cds_id} --end 30`, - false, - ) + p = new Shell(`${apollo} feature edit-coords -i ${cds_id} --end 30`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('exceeds the bounds of its parent')) // FIXME: Checks should be the same as before the invalid edit - p = new Shell(`${apollo} feature check ${P} -a checks.gff`) + p = new Shell(`${apollo} feature check -a checks.gff`) checks = JSON.parse(p.stdout) //assert.strictEqual(checks.length, 2) //assert.ok(p.stdout.includes('InternalStopCodon')) @@ -438,51 +424,46 @@ void describe('Test CLI', () => { void globalThis.itName('Add assembly from local fasta', () => { let p = new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a vv1 -e -f`, + `${apollo} assembly add-from-fasta test_data/tiny.fasta -a vv1 -e -f`, ) const out = JSON.parse(p.stdout) assert.ok(Object.keys(out.fileIds).includes('fa')) - p = new Shell(`${apollo} assembly get ${P} -a vv1`) + p = new Shell(`${apollo} assembly get -a vv1`) assert.ok(p.stdout.includes('vv1')) p = new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a vv1 -e`, + `${apollo} assembly add-from-fasta test_data/tiny.fasta -a vv1 -e`, false, ) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('Error: Assembly "vv1" already exists')) - p = new Shell( - `${apollo} assembly add-from-fasta ${P} na.fa -a vv1 -e -f`, - false, - ) + p = new Shell(`${apollo} assembly add-from-fasta na.fa -a vv1 -e -f`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('Input')) // Test default name - new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -e -f`, - ) - p = new Shell(`${apollo} assembly get ${P} -a tiny.fasta`) + new Shell(`${apollo} assembly add-from-fasta test_data/tiny.fasta -e -f`) + p = new Shell(`${apollo} assembly get -a tiny.fasta`) assert.ok(p.stdout.includes('tiny.fasta')) }) void globalThis.itName('Add assembly from external fasta', () => { let p = new Shell( - `${apollo} assembly add-from-fasta ${P} -a vv1 -f http://localhost:3131/volvox.fa.gz`, + `${apollo} assembly add-from-fasta -a vv1 -f http://localhost:3131/volvox.fa.gz`, ) const out = JSON.parse(p.stdout) assert.ok(Object.keys(out.externalLocation).includes('fa')) - p = new Shell(`${apollo} assembly get ${P} -a vv1`) + p = new Shell(`${apollo} assembly get -a vv1`) assert.ok(p.stdout.includes('vv1')) - p = new Shell(`${apollo} assembly sequence ${P} -a vv1 -r ctgA -s 1 -e 10`) + p = new Shell(`${apollo} assembly sequence -a vv1 -r ctgA -s 1 -e 10`) const seq = p.stdout.trim().split('\n') assert.strictEqual(seq[1], 'cattgttgcg') p = new Shell( - `${apollo} assembly add-from-fasta ${P} -a vv1 -f https://x.fa.gz --fai https://x.fa.gz.fai --gzi https://x.fa.gz.gzi`, + `${apollo} assembly add-from-fasta -a vv1 -f https://x.fa.gz --fai https://x.fa.gz.fai --gzi https://x.fa.gz.gzi`, false, ) assert.ok(p.returncode != 0) @@ -490,7 +471,7 @@ void describe('Test CLI', () => { void globalThis.itName('Detect missing external index', () => { const p = new Shell( - `${apollo} assembly add-from-fasta ${P} -a vv1 -f http://localhost:3131/tiny.fasta`, + `${apollo} assembly add-from-fasta -a vv1 -f http://localhost:3131/tiny.fasta`, false, ) assert.ok(p.returncode != 0) @@ -502,7 +483,7 @@ void describe('Test CLI', () => { void globalThis.itName( 'Editable sequence not allowed with external source', () => { - const cmd = `${apollo} assembly add-from-fasta ${P} -a vv1 -f http://localhost:3131/tiny.fasta.gz` + const cmd = `${apollo} assembly add-from-fasta -a vv1 -f http://localhost:3131/tiny.fasta.gz` new Shell(cmd) const p = new Shell(`${cmd} -e`, false) @@ -513,13 +494,13 @@ void describe('Test CLI', () => { void globalThis.itName('Edit feature from json', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, ) - let p = new Shell(`${apollo} feature search ${P} -a vv1 -t BAC`) + let p = new Shell(`${apollo} feature search -a vv1 -t BAC`) let out = JSON.parse(p.stdout).at(0) assert.strictEqual(out.type, 'BAC') - p = new Shell(`${apollo} assembly get ${P} -a vv1`) + p = new Shell(`${apollo} assembly get -a vv1`) const asm_id = JSON.parse(p.stdout).at(0)._id const req = [ @@ -533,29 +514,29 @@ void describe('Test CLI', () => { }, ] const j = JSON.stringify(req) - new Shell(`echo '${j}' | ${apollo} feature edit ${P} -j -`) - p = new Shell(`${apollo} feature search ${P} -a vv1 -t G_quartet`) + new Shell(`echo '${j}' | ${apollo} feature edit -j -`) + p = new Shell(`${apollo} feature search -a vv1 -t G_quartet`) out = JSON.parse(p.stdout).at(0) assert.strictEqual(out.type, 'G_quartet') }) void globalThis.itName('Edit feature type', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, ) // Get id of assembly named vv1 - let p = new Shell(`${apollo} assembly get ${P} -a vv1`) + let p = new Shell(`${apollo} assembly get -a vv1`) const asm_id = JSON.parse(p.stdout).at(0)._id // Get refseqs in assembly vv1 p = new Shell( - `${apollo} refseq get ${P} | jq '.[] | select(.assembly == "${asm_id}" and .name == "ctgA") | ._id'`, + `${apollo} refseq get | jq '.[] | select(.assembly == "${asm_id}" and .name == "ctgA") | ._id'`, ) const refseq = p.stdout.trim() // Get feature in vv1 - p = new Shell(`${apollo} feature get ${P} -r ${refseq}`) + p = new Shell(`${apollo} feature get -r ${refseq}`) const features = JSON.parse(p.stdout) assert.ok(features.length > 2) @@ -565,36 +546,36 @@ void describe('Test CLI', () => { const contig_id = contig.at(0)._id // Edit type of "contig" feature - p = new Shell(`${apollo} feature edit-type ${P} -i ${contig_id} -t region`) + p = new Shell(`${apollo} feature edit-type -i ${contig_id} -t region`) p = new Shell( - `${apollo} feature get ${P} -r ${refseq} | jq '.[] | select(._id == "${contig_id}")'`, + `${apollo} feature get -r ${refseq} | jq '.[] | select(._id == "${contig_id}")'`, ) contig = JSON.parse(p.stdout) assert.deepStrictEqual(contig.type, 'region') // Return current type - p = new Shell(`${apollo} feature edit-type ${P} -i ${contig_id}`) + p = new Shell(`${apollo} feature edit-type -i ${contig_id}`) assert.deepStrictEqual(p.stdout.trim(), 'region') }) void globalThis.itName('Edit feature coords', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, ) // Get id of assembly named vv1 - let p = new Shell(`${apollo} assembly get ${P} -a vv1`) + let p = new Shell(`${apollo} assembly get -a vv1`) const asm_id = JSON.parse(p.stdout).at(0)._id // Get refseqs in assembly vv1 p = new Shell( - `${apollo} refseq get ${P} | jq '.[] | select(.assembly == "${asm_id}" and .name == "ctgA") | ._id'`, + `${apollo} refseq get | jq '.[] | select(.assembly == "${asm_id}" and .name == "ctgA") | ._id'`, ) const refseq = p.stdout.trim() // Get feature in vv1 - p = new Shell(`${apollo} feature get ${P} -r ${refseq}`) + p = new Shell(`${apollo} feature get -r ${refseq}`) const features = JSON.parse(p.stdout) assert.ok(features.length > 2) @@ -604,35 +585,30 @@ void describe('Test CLI', () => { const contig_id = contig.at(0)._id // Edit start and end coordinates - new Shell(`${apollo} feature edit-coords ${P} -i ${contig_id} -s 80 -e 160`) - new Shell(`${apollo} feature edit-coords ${P} -i ${contig_id} -s 20 -e 100`) + new Shell(`${apollo} feature edit-coords -i ${contig_id} -s 80 -e 160`) + new Shell(`${apollo} feature edit-coords -i ${contig_id} -s 20 -e 100`) p = new Shell( - `${apollo} feature get ${P} -r ${refseq} | jq '.[] | select(._id == "${contig_id}")'`, + `${apollo} feature get -r ${refseq} | jq '.[] | select(._id == "${contig_id}")'`, ) contig = JSON.parse(p.stdout) assert.strictEqual(contig.min, 20 - 1) assert.strictEqual(contig.max, 100) + p = new Shell(`${apollo} feature edit-coords -i ${contig_id} -s 1 -e 1`) p = new Shell( - `${apollo} feature edit-coords ${P} -i ${contig_id} -s 1 -e 1`, - ) - p = new Shell( - `${apollo} feature get ${P} -r ${refseq} | jq '.[] | select(._id == "${contig_id}")'`, + `${apollo} feature get -r ${refseq} | jq '.[] | select(._id == "${contig_id}")'`, ) contig = JSON.parse(p.stdout) assert.strictEqual(contig.min, 0) assert.strictEqual(contig.max, 1) - p = new Shell( - `${apollo} feature edit-coords ${P} -i ${contig_id} -s 0`, - false, - ) + p = new Shell(`${apollo} feature edit-coords -i ${contig_id} -s 0`, false) assert.strictEqual(p.returncode, 2) assert.ok(p.stderr.includes('Coordinates must be greater than 0')) p = new Shell( - `${apollo} feature edit-coords ${P} -i ${contig_id} -s 10 -e 9`, + `${apollo} feature edit-coords -i ${contig_id} -s 10 -e 9`, false, ) assert.strictEqual(p.returncode, 2) @@ -650,199 +626,192 @@ void describe('Test CLI', () => { ) assert.ok(eden_gene) const mrna_id = Object.keys(eden_gene.children).at(0) - p = new Shell( - `${apollo} feature edit-coords ${P} -i ${mrna_id} -s 1`, - false, - ) + p = new Shell(`${apollo} feature edit-coords -i ${mrna_id} -s 1`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('exceeds the bounds of its parent')) }) void globalThis.itName('Edit attributes', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, ) // Get id of assembly named vv1 - let p = new Shell(`${apollo} assembly get ${P} -a vv1`) + let p = new Shell(`${apollo} assembly get -a vv1`) const asm_id = JSON.parse(p.stdout).at(0)._id p = new Shell( - `${apollo} refseq get ${P} | jq '.[] | select(.assembly == "${asm_id}" and .name == "ctgA") | ._id'`, + `${apollo} refseq get | jq '.[] | select(.assembly == "${asm_id}" and .name == "ctgA") | ._id'`, ) const refseq = p.stdout.trim() // Get feature in vv1 p = new Shell( - `${apollo} feature get ${P} -r ${refseq} | jq '.[] | select(.type == "contig") | ._id'`, + `${apollo} feature get -r ${refseq} | jq '.[] | select(.type == "contig") | ._id'`, ) const fid = p.stdout.trim() // Edit existing attribute value p = new Shell( - `${apollo} feature edit-attribute ${P} -i ${fid} -a source -v 'Eggs & Stuff'`, + `${apollo} feature edit-attribute -i ${fid} -a source -v 'Eggs & Stuff'`, ) - p = new Shell(`${apollo} feature edit-attribute ${P} -i ${fid} -a source`) + p = new Shell(`${apollo} feature edit-attribute -i ${fid} -a source`) let out = JSON.parse(p.stdout) assert.deepStrictEqual(out.at(0), `Eggs & Stuff`) // Add attribute - new Shell( - `${apollo} feature edit-attribute ${P} -i ${fid} -a newAttr -v stuff`, - ) - p = new Shell(`${apollo} feature edit-attribute ${P} -i ${fid} -a newAttr`) + new Shell(`${apollo} feature edit-attribute -i ${fid} -a newAttr -v stuff`) + p = new Shell(`${apollo} feature edit-attribute -i ${fid} -a newAttr`) assert.ok(p.stdout.includes('stuf')) // Non existing attr - p = new Shell(`${apollo} feature edit-attribute ${P} -i ${fid} -a NonExist`) + p = new Shell(`${apollo} feature edit-attribute -i ${fid} -a NonExist`) assert.deepStrictEqual(p.stdout.trim(), '') // List of values p = new Shell( - `${apollo} feature edit-attribute ${P} -i ${fid} -a newAttr -v A B C`, + `${apollo} feature edit-attribute -i ${fid} -a newAttr -v A B C`, ) - p = new Shell(`${apollo} feature edit-attribute ${P} -i ${fid} -a newAttr`) + p = new Shell(`${apollo} feature edit-attribute -i ${fid} -a newAttr`) out = JSON.parse(p.stdout) assert.deepStrictEqual(out, ['A', 'B', 'C']) // Delete attribute - new Shell(`${apollo} feature edit-attribute ${P} -i ${fid} -a newAttr -d`) - p = new Shell(`${apollo} feature edit-attribute ${P} -i ${fid} -a newAttr`) + new Shell(`${apollo} feature edit-attribute -i ${fid} -a newAttr -d`) + p = new Shell(`${apollo} feature edit-attribute -i ${fid} -a newAttr`) assert.deepStrictEqual(p.stdout.trim(), '') // Delete again is ok - new Shell(`${apollo} feature edit-attribute ${P} -i ${fid} -a newAttr -d`) + new Shell(`${apollo} feature edit-attribute -i ${fid} -a newAttr -d`) // Special fields p = new Shell( - `${apollo} feature edit-attribute ${P} -i ${fid} -a 'Gene Ontology' -v GO:0051728 GO:0019090`, + `${apollo} feature edit-attribute -i ${fid} -a 'Gene Ontology' -v GO:0051728 GO:0019090`, ) p = new Shell( - `${apollo} feature edit-attribute ${P} -i ${fid} -a 'Gene Ontology'`, + `${apollo} feature edit-attribute -i ${fid} -a 'Gene Ontology'`, ) out = JSON.parse(p.stdout) assert.deepStrictEqual(out, ['GO:0051728', 'GO:0019090']) // This should fail p = new Shell( - `${apollo} feature edit-attribute ${P} -i ${fid} -a 'Gene Ontology' -v FOOBAR`, + `${apollo} feature edit-attribute -i ${fid} -a 'Gene Ontology' -v FOOBAR`, ) }) void globalThis.itName('Search features', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, ) new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv2 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv2 -f`, ) - let p = new Shell(`${apollo} feature search ${P} -a vv1 vv2 -t EDEN`) + let p = new Shell(`${apollo} feature search -a vv1 vv2 -t EDEN`) let out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) assert.ok(p.stdout.includes('EDEN')) - p = new Shell(`${apollo} feature search ${P} -t EDEN`) + p = new Shell(`${apollo} feature search -t EDEN`) out = JSON.parse(p.stdout) assert.ok(out.length >= 2) - p = new Shell(`${apollo} feature search ${P} -a vv1 -t EDEN`) + p = new Shell(`${apollo} feature search -a vv1 -t EDEN`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) assert.ok(p.stdout.includes('EDEN')) - p = new Shell(`${apollo} feature search ${P} -a foobar -t EDEN`) + p = new Shell(`${apollo} feature search -a foobar -t EDEN`) assert.strictEqual('[]', p.stdout.trim()) assert.ok(p.stderr.includes('Warning')) - p = new Shell(`${apollo} feature search ${P} -a vv1 -t foobarspam`) + p = new Shell(`${apollo} feature search -a vv1 -t foobarspam`) assert.deepStrictEqual(p.stdout.trim(), '[]') // It searches attributes values, not attribute names - p = new Shell(`${apollo} feature search ${P} -a vv1 -t multivalue`) + p = new Shell(`${apollo} feature search -a vv1 -t multivalue`) assert.deepStrictEqual(p.stdout.trim(), '[]') // Search feature type - p = new Shell(`${apollo} feature search ${P} -a vv1 -t contig`) + p = new Shell(`${apollo} feature search -a vv1 -t contig`) assert.ok(p.stdout.includes('"type": "contig"')) // Search source (which in fact is an attribute) - p = new Shell(`${apollo} feature search ${P} -a vv1 -t someExample`) + p = new Shell(`${apollo} feature search -a vv1 -t someExample`) assert.ok(p.stdout.includes('SomeContig')) // Case insensitive - p = new Shell(`${apollo} feature search ${P} -a vv1 -t SOMEexample`) + p = new Shell(`${apollo} feature search -a vv1 -t SOMEexample`) assert.ok(p.stdout.includes('SomeContig')) // No partial word match - p = new Shell(`${apollo} feature search ${P} -a vv1 -t Fingerpri`) + p = new Shell(`${apollo} feature search -a vv1 -t Fingerpri`) assert.deepStrictEqual(p.stdout.trim(), '[]') // Match full word not necessarily full value - p = new Shell(`${apollo} feature search ${P} -a vv1 -t Fingerprinted`) + p = new Shell(`${apollo} feature search -a vv1 -t Fingerprinted`) assert.ok(p.stdout.includes('Fingerprinted')) // Does not search contig names (reference sequence name) - p = new Shell(`${apollo} feature search ${P} -a vv1 -t ctgB`) + p = new Shell(`${apollo} feature search -a vv1 -t ctgB`) assert.deepStrictEqual(p.stdout.trim(), '[]') // Does not match common words (?) ... - p = new Shell(`${apollo} feature search ${P} -a vv1 -t with`) + p = new Shell(`${apollo} feature search -a vv1 -t with`) assert.deepStrictEqual(p.stdout.trim(), '[]') // ...But "fake" is ok - p = new Shell(`${apollo} feature search ${P} -a vv1 -t fake`) + p = new Shell(`${apollo} feature search -a vv1 -t fake`) assert.ok(p.stdout.includes('FakeSNP1')) // ...or a single unusual letter - p = new Shell(`${apollo} feature search ${P} -a vv1 -t Q`) + p = new Shell(`${apollo} feature search -a vv1 -t Q`) assert.ok(p.stdout.includes('"Q"')) }) void globalThis.itName('Get feature by indexed ID', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, ) new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv2 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv2 -f`, ) // Search multiple assemblies - let p = new Shell(`${apollo} feature get-indexed-id ${P} MyGene -a vv1 vv2`) + let p = new Shell(`${apollo} feature get-indexed-id MyGene -a vv1 vv2`) let out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) assert.ok(p.stdout.includes('MyGene')) // Specifying no assembly defaults to searching all assemblies - p = new Shell(`${apollo} feature get-indexed-id ${P} MyGene`) + p = new Shell(`${apollo} feature get-indexed-id MyGene`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) assert.ok(p.stdout.includes('MyGene')) // Search single assembly - p = new Shell(`${apollo} feature get-indexed-id ${P} MyGene -a vv1`) + p = new Shell(`${apollo} feature get-indexed-id MyGene -a vv1`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) assert.ok(p.stdout.includes('MyGene')) // Warn on unknown assembly - p = new Shell(`${apollo} feature get-indexed-id ${P} EDEN -a foobar`) + p = new Shell(`${apollo} feature get-indexed-id EDEN -a foobar`) assert.strictEqual('[]', p.stdout.trim()) assert.ok(p.stderr.includes('Warning')) // Return empty array with no matches - p = new Shell(`${apollo} feature get-indexed-id ${P} foobarspam -a vv1`) + p = new Shell(`${apollo} feature get-indexed-id foobarspam -a vv1`) assert.deepStrictEqual(p.stdout.trim(), '[]') // Gets subfeature - p = new Shell(`${apollo} feature get-indexed-id ${P} myCDS.1 -a vv1`) + p = new Shell(`${apollo} feature get-indexed-id myCDS.1 -a vv1`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) assert.ok(out.at(0)?.type === 'CDS') // Gets top-level feature from subfeature id - p = new Shell( - `${apollo} feature get-indexed-id ${P} myCDS.1 -a vv1 --topLevel`, - ) + p = new Shell(`${apollo} feature get-indexed-id myCDS.1 -a vv1 --topLevel`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) assert.ok(out.at(0)?.type === 'gene') @@ -850,45 +819,45 @@ void describe('Test CLI', () => { void globalThis.itName('Delete features', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, ) - let p = new Shell(`${apollo} feature search ${P} -a vv1 -t EDEN`) + let p = new Shell(`${apollo} feature search -a vv1 -t EDEN`) const fid = JSON.parse(p.stdout).at(0)._id - p = new Shell(`${apollo} feature delete ${P} -i ${fid} --dry-run`) + p = new Shell(`${apollo} feature delete -i ${fid} --dry-run`) assert.ok(p.stdout.includes(fid)) - new Shell(`${apollo} feature delete ${P} -i ${fid}`) - p = new Shell(`${apollo} feature search ${P} -a vv1 -t EDEN`) + new Shell(`${apollo} feature delete -i ${fid}`) + p = new Shell(`${apollo} feature search -a vv1 -t EDEN`) assert.deepStrictEqual(p.stdout.trim(), '[]') - p = new Shell(`${apollo} feature delete ${P} -i ${fid}`, false) + p = new Shell(`${apollo} feature delete -i ${fid}`, false) assert.strictEqual(p.returncode, 1) assert.ok( p.stderr.includes('The following featureId was not found in database'), ) - p = new Shell(`${apollo} feature delete ${P} --force -i ${fid}`) + p = new Shell(`${apollo} feature delete --force -i ${fid}`) assert.strictEqual(p.returncode, 0) }) void globalThis.itName('Add child features', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, ) - let p = new Shell(`${apollo} feature search ${P} -a vv1 -t contig`) + let p = new Shell(`${apollo} feature search -a vv1 -t contig`) const fid = JSON.parse(p.stdout).at(0)._id new Shell( - `${apollo} feature add-child ${P} -i ${fid} -s 10 -e 20 -t contig_read`, + `${apollo} feature add-child -i ${fid} -s 10 -e 20 -t contig_read`, ) - p = new Shell(`${apollo} feature search ${P} -a vv1 -t contig_read`) + p = new Shell(`${apollo} feature search -a vv1 -t contig_read`) assert.ok(p.stdout.includes('contig_read')) assert.ok(p.stdout.includes('"min": 9')) assert.ok(p.stdout.includes('"max": 20')) p = new Shell( - `${apollo} feature add-child ${P} -i ${fid} -s 10 -e 2000 -t contig_read`, + `${apollo} feature add-child -i ${fid} -s 10 -e 2000 -t contig_read`, false, ) assert.ok(p.returncode != 0) @@ -896,7 +865,7 @@ void describe('Test CLI', () => { // Should this fail? p = new Shell( - `${apollo} feature add-child ${P} -i ${fid} -s 10 -e 20 -t FOOBAR`, + `${apollo} feature add-child -i ${fid} -s 10 -e 20 -t FOOBAR`, false, ) assert.strictEqual(p.returncode, 0) @@ -904,116 +873,114 @@ void describe('Test CLI', () => { void globalThis.itName('Import features', () => { new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a vv1 -e -f`, + `${apollo} assembly add-from-fasta test_data/tiny.fasta -a vv1 -e -f`, ) - new Shell(`${apollo} feature import ${P} test_data/tiny.fasta.gff3 -a vv1`) - let p = new Shell(`${apollo} feature search ${P} -a vv1 -t contig`) + new Shell(`${apollo} feature import test_data/tiny.fasta.gff3 -a vv1`) + let p = new Shell(`${apollo} feature search -a vv1 -t contig`) let out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) // Import again: Add to existing feature - p = new Shell( - `${apollo} feature import ${P} test_data/tiny.fasta.gff3 -a vv1`, - ) - p = new Shell(`${apollo} feature search ${P} -a vv1 -t contig`) + p = new Shell(`${apollo} feature import test_data/tiny.fasta.gff3 -a vv1`) + p = new Shell(`${apollo} feature search -a vv1 -t contig`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 4) - // Import again: delete ${P} existing + // Import again: delete existing p = new Shell( - `${apollo} feature import ${P} -d test_data/tiny.fasta.gff3 -a vv1`, + `${apollo} feature import -d test_data/tiny.fasta.gff3 -a vv1`, ) - p = new Shell(`${apollo} feature search ${P} -a vv1 -t contig`) + p = new Shell(`${apollo} feature search -a vv1 -t contig`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) - p = new Shell(`${apollo} assembly delete ${P} -a vv2`) + p = new Shell(`${apollo} assembly delete -a vv2`) p = new Shell( - `${apollo} feature import ${P} test_data/tiny.fasta.gff3 -a vv2`, + `${apollo} feature import test_data/tiny.fasta.gff3 -a vv2`, false, ) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('Assembly "vv2" does not exist')) - p = new Shell(`${apollo} feature import ${P} foo.gff3 -a vv1`, false) + p = new Shell(`${apollo} feature import foo.gff3 -a vv1`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('File "foo.gff3" does not exist')) }) void globalThis.itName('Copy feature', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a source -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a source -f`, ) new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a dest -e -f`, + `${apollo} assembly add-from-fasta test_data/tiny.fasta -a dest -e -f`, ) new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a dest2 -e -f`, + `${apollo} assembly add-from-fasta test_data/tiny.fasta -a dest2 -e -f`, ) - let p = new Shell(`${apollo} feature search ${P} -a source -t contig`) + let p = new Shell(`${apollo} feature search -a source -t contig`) const fid = JSON.parse(p.stdout).at(0)._id - new Shell(`${apollo} feature copy ${P} -i ${fid} -r ctgA -a dest -s 1`) - p = new Shell(`${apollo} feature search ${P} -a dest -t contig`) + new Shell(`${apollo} feature copy -i ${fid} -r ctgA -a dest -s 1`) + p = new Shell(`${apollo} feature search -a dest -t contig`) let out = JSON.parse(p.stdout).at(0) assert.strictEqual(out.min, 0) assert.strictEqual(out.max, 50) // RefSeq id does not need assembly - p = new Shell(`${apollo} refseq get ${P} -a dest2`) + p = new Shell(`${apollo} refseq get -a dest2`) const destRefSeq = JSON.parse(p.stdout).find( (x: any) => x.name === 'ctgA', )._id - p = new Shell(`${apollo} feature copy ${P} -i ${fid} -r ${destRefSeq} -s 2`) - p = new Shell(`${apollo} feature search ${P} -a dest2 -t contig`) + p = new Shell(`${apollo} feature copy -i ${fid} -r ${destRefSeq} -s 2`) + p = new Shell(`${apollo} feature search -a dest2 -t contig`) out = JSON.parse(p.stdout).at(0) assert.strictEqual(out.min, 1) assert.strictEqual(out.max, 51) // Copy to same assembly - new Shell(`${apollo} feature copy ${P} -i ${fid} -r ctgA -a source -s 10`) - p = new Shell(`${apollo} feature search ${P} -a source -t contig`) + new Shell(`${apollo} feature copy -i ${fid} -r ctgA -a source -s 10`) + p = new Shell(`${apollo} feature search -a source -t contig`) out = JSON.parse(p.stdout) // Copy non-existant feature or refseq p = new Shell( - `${apollo} feature copy ${P} -i FOOBAR -r ctgA -a dest -s 1`, + `${apollo} feature copy -i FOOBAR -r ctgA -a dest -s 1`, false, ) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('ERROR')) p = new Shell( - `${apollo} feature copy ${P} -i ${fid} -r FOOBAR -a dest -s 1`, + `${apollo} feature copy -i ${fid} -r FOOBAR -a dest -s 1`, false, ) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('No reference')) // Ambiguous refseq - p = new Shell(`${apollo} feature copy ${P} -i ${fid} -r ctgA -s 1`, false) + p = new Shell(`${apollo} feature copy -i ${fid} -r ctgA -s 1`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('more than one')) }) void globalThis.itName('Get changes', () => { new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a myAssembly -e -f`, + `${apollo} assembly add-from-fasta test_data/tiny.fasta -a myAssembly -e -f`, ) new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a yourAssembly -e -f`, + `${apollo} assembly add-from-fasta test_data/tiny.fasta -a yourAssembly -e -f`, ) new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a ourAssembly -e -f`, + `${apollo} assembly add-from-fasta test_data/tiny.fasta -a ourAssembly -e -f`, ) - let p = new Shell(`${apollo} change get ${P}`) + let p = new Shell(`${apollo} change get`) JSON.parse(p.stdout) assert.ok(p.stdout.includes('myAssembly')) assert.ok(p.stdout.includes('yourAssembly')) - p = new Shell(`${apollo} change get ${P} -a myAssembly ourAssembly`) + p = new Shell(`${apollo} change get -a myAssembly ourAssembly`) assert.ok(p.stdout.includes('myAssembly')) assert.ok(p.stdout.includes('ourAssembly')) assert.ok(p.stdout.includes('yourAssembly') == false) @@ -1022,30 +989,30 @@ void describe('Test CLI', () => { // returned because the assemblies collection doesn't contain that name // anymore. Ideally you should still be able to get changes by name? new Shell( - `${apollo} assembly delete ${P} -a myAssembly yourAssembly ourAssembly`, + `${apollo} assembly delete -a myAssembly yourAssembly ourAssembly`, ) - p = new Shell(`${apollo} change get ${P} -a myAssembly`) + p = new Shell(`${apollo} change get -a myAssembly`) const out = JSON.parse(p.stdout) assert.strictEqual(out.length, 0) }) void globalThis.itName('Get sequence', () => { new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a v1 -e -f`, + `${apollo} assembly add-from-fasta test_data/tiny.fasta -a v1 -e -f`, ) new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a v2 -e -f`, + `${apollo} assembly add-from-fasta test_data/tiny.fasta -a v2 -e -f`, ) - let p = new Shell(`${apollo} assembly sequence ${P} -a nonExistant`, false) + let p = new Shell(`${apollo} assembly sequence -a nonExistant`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('returned 0 assemblies')) - p = new Shell(`${apollo} assembly sequence ${P} -a v1 -s 0`, false) + p = new Shell(`${apollo} assembly sequence -a v1 -s 0`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('must be greater than 0')) - p = new Shell(`${apollo} assembly sequence ${P} -a v1`) + p = new Shell(`${apollo} assembly sequence -a v1`) let seq = p.stdout.trim().split('\n') assert.strictEqual(seq.length, 25) assert.deepStrictEqual(seq.at(0), '>ctgA:1..420') @@ -1057,40 +1024,40 @@ void describe('Test CLI', () => { assert.deepStrictEqual(seq.at(7), '>ctgB:1..800') assert.deepStrictEqual(seq.at(-1), 'ttggtcgctccgttgtaccc') - p = new Shell(`${apollo} assembly sequence ${P} -a v1 -r ctgB -s 1 -e 1`) + p = new Shell(`${apollo} assembly sequence -a v1 -r ctgB -s 1 -e 1`) seq = p.stdout.split('\n') assert.deepStrictEqual(seq.at(0), '>ctgB:1..1') assert.deepStrictEqual(seq.at(1), 'A') - p = new Shell(`${apollo} assembly sequence ${P} -a v1 -r ctgB -s 2 -e 4`) + p = new Shell(`${apollo} assembly sequence -a v1 -r ctgB -s 2 -e 4`) seq = p.stdout.split('\n') assert.deepStrictEqual(seq.at(0), '>ctgB:2..4') assert.deepStrictEqual(seq.at(1), 'CAT') - p = new Shell(`${apollo} assembly sequence ${P} -r ctgB`, false) + p = new Shell(`${apollo} assembly sequence -r ctgB`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('found in more than one')) }) void globalThis.itName('Get feature by id', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a v1 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a v1 -f`, ) - let p = new Shell(`${apollo} feature get ${P} -a v1`) + let p = new Shell(`${apollo} feature get -a v1`) const ff = JSON.parse(p.stdout) const x1 = ff.at(0)._id const x2 = ff.at(1)._id - p = new Shell(`${apollo} feature get-id ${P} -i ${x1} ${x1} ${x2}`) + p = new Shell(`${apollo} feature get-id -i ${x1} ${x1} ${x2}`) let out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) assert.deepStrictEqual(out.at(0)._id, x1) assert.deepStrictEqual(out.at(1)._id, x2) - p = new Shell(`${apollo} feature get-id ${P} -i FOOBAR`) + p = new Shell(`${apollo} feature get-id -i FOOBAR`) assert.deepStrictEqual(p.stdout.trim(), '[]') - p = new Shell(`echo -e '${x1} \n ${x2}' | ${apollo} feature get-id ${P}`) + p = new Shell(`echo -e '${x1} \n ${x2}' | ${apollo} feature get-id`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) }) @@ -1099,52 +1066,52 @@ void describe('Test CLI', () => { // TODO: Improve tests once more checks exist (currently there is only // CDSCheck) new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a v1 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a v1 -f`, ) // Test view available check type - let p = new Shell(`${apollo} assembly check ${P}`) + let p = new Shell(`${apollo} assembly check`) let out = JSON.parse(p.stdout) assert.ok(p.stdout.includes('CDSCheck')) assert.ok(p.stdout.includes('TranscriptCheck')) const cdsCheckId = out.find((x: any) => x.name === 'CDSCheck')._id // Test view checks set for assembly - p = new Shell(`${apollo} assembly check ${P} -a v1`) + p = new Shell(`${apollo} assembly check -a v1`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) // Test non-existant assembly - p = new Shell(`${apollo} assembly check ${P} -a non-existant`, false) + p = new Shell(`${apollo} assembly check -a non-existant`, false) assert.strictEqual(p.returncode, 1) assert.ok(p.stderr.includes('non-existant')) // Test non-existant check - p = new Shell(`${apollo} assembly check ${P} -a v1 -c not-a-check`, false) + p = new Shell(`${apollo} assembly check -a v1 -c not-a-check`, false) assert.strictEqual(p.returncode, 1) assert.ok(p.stderr.includes('not-a-check')) // Test add checks. Test check is added as opposed to replacing current // checks with input list - new Shell(`${apollo} assembly check ${P} -a v1 -c CDSCheck CDSCheck`) - p = new Shell(`${apollo} assembly check ${P} -a v1`) + new Shell(`${apollo} assembly check -a v1 -c CDSCheck CDSCheck`) + p = new Shell(`${apollo} assembly check -a v1`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) assert.deepStrictEqual(out.at(0).name, 'CDSCheck') // Works also with check id new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a v2 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a v2 -f`, ) - new Shell(`${apollo} assembly check ${P} -a v2 -c ${cdsCheckId}`) - p = new Shell(`${apollo} assembly check ${P} -a v2`) + new Shell(`${apollo} assembly check -a v2 -c ${cdsCheckId}`) + p = new Shell(`${apollo} assembly check -a v2`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) assert.deepStrictEqual(out.at(0).name, 'CDSCheck') // Delete check - new Shell(`${apollo} assembly check ${P} -a v1 -d -c CDSCheck`) - p = new Shell(`${apollo} assembly check ${P} -a v1`) + new Shell(`${apollo} assembly check -a v1 -d -c CDSCheck`) + p = new Shell(`${apollo} assembly check -a v1`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) assert.ok(!p.stdout.includes('CDSCheck')) @@ -1153,10 +1120,10 @@ void describe('Test CLI', () => { void globalThis.itName('Feature checks', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a v1 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a v1 -f`, ) - new Shell(`${apollo} assembly check ${P} -a v1 -c CDSCheck`) - let p = new Shell(`${apollo} feature check ${P} -a v1`) + new Shell(`${apollo} assembly check -a v1 -c CDSCheck`) + let p = new Shell(`${apollo} feature check -a v1`) const out = JSON.parse(p.stdout) assert.ok(out.length > 1) assert.ok(p.stdout.includes('InternalStopCodon')) @@ -1167,19 +1134,17 @@ void describe('Test CLI', () => { // Retrieve by feature id const xid = [...ids].join(' ') - p = new Shell(`${apollo} feature check ${P} -i ${xid}`) + p = new Shell(`${apollo} feature check -i ${xid}`) assert.ok(p.stdout.includes('InternalStopCodon')) }) void globalThis.itName('Feature checks indexed', () => { new Shell( - `${apollo} assembly add-from-fasta ${P} -a v1 test_data/tiny.fasta.gz -f`, - ) - new Shell(`${apollo} assembly check ${P} -a v1 -c CDSCheck`) - new Shell( - `${apollo} feature import ${P} -a v1 test_data/tiny.fasta.gff3 -d`, + `${apollo} assembly add-from-fasta -a v1 test_data/tiny.fasta.gz -f`, ) - let p = new Shell(`${apollo} feature check ${P} -a v1`) + new Shell(`${apollo} assembly check -a v1 -c CDSCheck`) + new Shell(`${apollo} feature import -a v1 test_data/tiny.fasta.gff3 -d`) + let p = new Shell(`${apollo} feature check -a v1`) const out = JSON.parse(p.stdout) assert.ok(out.length > 1) assert.ok(p.stdout.includes('InternalStopCodon')) @@ -1190,7 +1155,7 @@ void describe('Test CLI', () => { // Retrieve by feature id const xid = [...ids].join(' ') - p = new Shell(`${apollo} feature check ${P} -i ${xid}`) + p = new Shell(`${apollo} feature check -i ${xid}`) assert.ok(p.stdout.includes('InternalStopCodon')) }) @@ -1198,27 +1163,25 @@ void describe('Test CLI', () => { 'Delete check results when unregistering a check', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a v1 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a v1 -f`, ) - let p = new Shell(`${apollo} feature check ${P} -a v1`) + let p = new Shell(`${apollo} feature check -a v1`) let checkResults = JSON.parse(p.stdout) as CheckResultSnapshot[] assert.ok(checkResults.length > 1) // Delete all checks and consequently delete all check results - p = new Shell(`${apollo} assembly check ${P} -a v1`) + p = new Shell(`${apollo} assembly check -a v1`) const checkNames = (JSON.parse(p.stdout) as CheckResultSnapshot[]).map( (x) => x.name, ) - new Shell( - `${apollo} assembly check ${P} -a v1 -d -c ${checkNames.join(' ')}`, - ) - p = new Shell(`${apollo} feature check ${P} -a v1`) + new Shell(`${apollo} assembly check -a v1 -d -c ${checkNames.join(' ')}`) + p = new Shell(`${apollo} feature check -a v1`) checkResults = JSON.parse(p.stdout) assert.deepEqual(checkResults.length, 0) // Put one check back - new Shell(`${apollo} assembly check ${P} -a v1 -c CDSCheck`) - p = new Shell(`${apollo} feature check ${P} -a v1`) + new Shell(`${apollo} assembly check -a v1 -c CDSCheck`) + p = new Shell(`${apollo} feature check -a v1`) checkResults = JSON.parse(p.stdout) assert.ok(checkResults.length > 0) assert.deepEqual( @@ -1229,20 +1192,20 @@ void describe('Test CLI', () => { ) void globalThis.itName('User', () => { - let p = new Shell(`${apollo} user get ${P}`) + let p = new Shell(`${apollo} user get`) let out = JSON.parse(p.stdout) assert.ok(out.length > 0) - p = new Shell(`${apollo} user get ${P} -r admin`) + p = new Shell(`${apollo} user get -r admin`) const out2 = JSON.parse(p.stdout) assert.ok(out.length > 0) assert.ok(out.length > out2.length) - p = new Shell(`${apollo} user get ${P} -r admin -u root`) + p = new Shell(`${apollo} user get -r admin -u root`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) - p = new Shell(`${apollo} user get ${P} -r readOnly -u root`) + p = new Shell(`${apollo} user get -r readOnly -u root`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 0) }) @@ -1302,22 +1265,22 @@ void describe('Test CLI', () => { void globalThis.itName('Refname alias configuration', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a asm1 -f`, + `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a asm1 -f`, ) - let p = new Shell(`${apollo} assembly get ${P} -a asm1`) + let p = new Shell(`${apollo} assembly get -a asm1`) assert.ok(p.stdout.includes('asm1')) assert.ok(p.stdout.includes('asm2') == false) const asm_id = JSON.parse(p.stdout)[0]._id p = new Shell( - `${apollo} refseq add-alias ${P} test_data/alias.txt -a asm2`, + `${apollo} refseq add-alias test_data/alias.txt -a asm2`, false, ) assert.ok(p.stderr.includes('Assembly asm2 not found')) p = new Shell( - `${apollo} refseq add-alias ${P} test_data/alias.txt -a asm1`, + `${apollo} refseq add-alias test_data/alias.txt -a asm1`, false, ) assert.ok( @@ -1326,7 +1289,7 @@ void describe('Test CLI', () => { ), ) - p = new Shell(`${apollo} refseq get ${P}`) + p = new Shell(`${apollo} refseq get`) const refseq = JSON.parse(p.stdout.trim()) const vv1ref = refseq.filter((x: any) => x.assembly === asm_id) const refname_aliases: Record = {} @@ -1350,30 +1313,30 @@ void describe('Test CLI', () => { // Works locally but fails on github void globalThis.itName('Login', () => { // This should wait for user's input - const p = new Shell(`${apollo} login ${P}`, false, 5000) + const p = new Shell(`${apollo} login`, false, 5000) assert.ok(p.returncode != 0) // This should be ok - new Shell(`${apollo} login ${P} --force`, true, 5000) + new Shell(`${apollo} login --force`, true, 5000) }) void globalThis.itName('File upload', () => { - let p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta`) + let p = new Shell(`${apollo} file upload test_data/tiny.fasta`) let out = JSON.parse(p.stdout) assert.deepStrictEqual(out.type, 'text/x-fasta') assert.ok(out._id) - p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta`) + p = new Shell(`${apollo} file upload test_data/tiny.fasta`) out = JSON.parse(p.stdout) assert.deepStrictEqual(out.type, 'text/x-fasta') - p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta.gff3`) + p = new Shell(`${apollo} file upload test_data/tiny.fasta.gff3`) out = JSON.parse(p.stdout) assert.deepStrictEqual(out.type, 'text/x-gff3') - p = new Shell(`${apollo} file upload ${P} test_data/guest.yaml`, false) + p = new Shell(`${apollo} file upload test_data/guest.yaml`, false) assert.ok(p.returncode != 0) - p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta.gz`, false) + p = new Shell(`${apollo} file upload test_data/tiny.fasta.gz`, false) assert.ok(p.stderr.includes('it may be gzip or bgzip compressed')) assert.ok(p.returncode != 0) }) @@ -1384,43 +1347,43 @@ void describe('Test CLI', () => { const md5 = crypto.createHash('md5').update(gz).digest('hex') const p = new Shell( - `${apollo} file upload ${P} test_data/tiny.fasta.gz -t text/x-fasta`, + `${apollo} file upload test_data/tiny.fasta.gz -t text/x-fasta`, ) const out = JSON.parse(p.stdout) assert.strictEqual(out.checksum, md5) - new Shell(`${apollo} assembly add-from-fasta ${P} -e -f ${out._id}`) + new Shell(`${apollo} assembly add-from-fasta -e -f ${out._id}`) }) void globalThis.itName('Add assembly gzip', () => { // Autodetect format new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta.gz -e -f -a vv1`, + `${apollo} assembly add-from-fasta test_data/tiny.fasta.gz -e -f -a vv1`, ) - let p = new Shell(`${apollo} assembly sequence ${P} -a vv1`) + let p = new Shell(`${apollo} assembly sequence -a vv1`) assert.ok(p.stdout.startsWith('>')) assert.ok(p.stdout.includes('cattgttgcggagttgaaca')) // Skip autodetect fs.copyFileSync('test_data/tiny.fasta', 'test_data/tmp.gz') new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tmp.gz -e -f -a vv1 --decompressed`, + `${apollo} assembly add-from-fasta test_data/tmp.gz -e -f -a vv1 --decompressed`, ) - p = new Shell(`${apollo} assembly sequence ${P} -a vv1`) + p = new Shell(`${apollo} assembly sequence -a vv1`) assert.ok(p.stdout.startsWith('>')) assert.ok(p.stdout.includes('cattgttgcggagttgaaca')) fs.unlinkSync('test_data/tmp.gz') fs.copyFileSync('test_data/tiny.fasta.gz', 'test_data/fasta.tmp') new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/fasta.tmp -e -f -a vv1 --gzip`, + `${apollo} assembly add-from-fasta test_data/fasta.tmp -e -f -a vv1 --gzip`, ) - p = new Shell(`${apollo} assembly sequence ${P} -a vv1`) + p = new Shell(`${apollo} assembly sequence -a vv1`) assert.ok(p.stdout.startsWith('>')) assert.ok(p.stdout.includes('cattgttgcggagttgaaca')) // Autodetect false positive p = new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/fasta.tmp -e -f -a vv1`, + `${apollo} assembly add-from-fasta test_data/fasta.tmp -e -f -a vv1`, false, ) assert.ok(p.returncode != 0) @@ -1429,15 +1392,13 @@ void describe('Test CLI', () => { void globalThis.itName('Add editable assembly', () => { // It would be good to check that really there was no sequence loading - new Shell( - `${apollo} assembly add-from-fasta ${P} -f test_data/tiny.fasta.gz`, - ) - let p = new Shell(`${apollo} assembly sequence ${P} -a tiny.fasta.gz`) + new Shell(`${apollo} assembly add-from-fasta -f test_data/tiny.fasta.gz`) + let p = new Shell(`${apollo} assembly sequence -a tiny.fasta.gz`) assert.ok(p.stdout.startsWith('>')) assert.ok(p.stdout.includes('cattgttgcggagttgaaca')) p = new Shell( - `${apollo} assembly add-from-fasta ${P} -f test_data/tiny.fasta`, + `${apollo} assembly add-from-fasta -f test_data/tiny.fasta`, false, ) assert.ok(p.returncode != 0) @@ -1445,9 +1406,9 @@ void describe('Test CLI', () => { // Setting --gzi & --fai new Shell( - `${apollo} assembly add-from-fasta ${P} -f test_data/tiny2.fasta.gz --gzi test_data/tiny.fasta.gz.gzi --fai test_data/tiny.fasta.gz.fai`, + `${apollo} assembly add-from-fasta -f test_data/tiny2.fasta.gz --gzi test_data/tiny.fasta.gz.gzi --fai test_data/tiny.fasta.gz.fai`, ) - p = new Shell(`${apollo} assembly sequence ${P} -a tiny2.fasta.gz`) + p = new Shell(`${apollo} assembly sequence -a tiny2.fasta.gz`) assert.ok(p.stdout.startsWith('>')) assert.ok(p.stdout.includes('cattgttgcggagttgaaca')) }) @@ -1455,60 +1416,60 @@ void describe('Test CLI', () => { void globalThis.itName('Add assembly from file ids not editable', () => { // Upload and get Ids for: bgzip fasta, fai and gzi let p = new Shell( - `${apollo} file upload ${P} test_data/tiny.fasta.gz -t application/x-bgzip-fasta`, + `${apollo} file upload test_data/tiny.fasta.gz -t application/x-bgzip-fasta`, ) const fastaId = JSON.parse(p.stdout)._id - p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta.gz.fai`) + p = new Shell(`${apollo} file upload test_data/tiny.fasta.gz.fai`) const faiId = JSON.parse(p.stdout)._id - p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta.gz.gzi`) + p = new Shell(`${apollo} file upload test_data/tiny.fasta.gz.gzi`) const gziId = JSON.parse(p.stdout)._id new Shell( - `${apollo} assembly add-from-fasta ${P} -f ${fastaId} --fai test_data/tiny.fasta.gz.fai --gzi test_data/tiny.fasta.gz.gzi`, + `${apollo} assembly add-from-fasta -f ${fastaId} --fai test_data/tiny.fasta.gz.fai --gzi test_data/tiny.fasta.gz.gzi`, ) - p = new Shell(`${apollo} assembly sequence ${P} -a ${fastaId}`) + p = new Shell(`${apollo} assembly sequence -a ${fastaId}`) assert.ok(p.stdout.startsWith('>')) assert.ok(p.stdout.includes('cattgttgcggagttgaaca')) new Shell( - `${apollo} assembly add-from-fasta ${P} -f ${fastaId} --fai ${faiId} --gzi ${gziId}`, + `${apollo} assembly add-from-fasta -f ${fastaId} --fai ${faiId} --gzi ${gziId}`, ) - p = new Shell(`${apollo} assembly sequence ${P} -a ${fastaId}`) + p = new Shell(`${apollo} assembly sequence -a ${fastaId}`) assert.ok(p.stdout.startsWith('>')) }) void globalThis.itName('Add assembly from file id', () => { - let p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta`) + let p = new Shell(`${apollo} file upload test_data/tiny.fasta`) const fid = JSON.parse(p.stdout)._id - p = new Shell(`${apollo} assembly add-from-fasta ${P} ${fid} -a up -e -f`) + p = new Shell(`${apollo} assembly add-from-fasta ${fid} -a up -e -f`) const out = JSON.parse(p.stdout) assert.deepStrictEqual(out.name, 'up') assert.deepStrictEqual(out.fileIds.fa, fid) }) void globalThis.itName('Get files', () => { - new Shell(`${apollo} file upload ${P} test_data/tiny.fasta`) - let p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta`) + new Shell(`${apollo} file upload test_data/tiny.fasta`) + let p = new Shell(`${apollo} file upload test_data/tiny.fasta`) const fid = JSON.parse(p.stdout)._id - p = new Shell(`${apollo} file get ${P}`) + p = new Shell(`${apollo} file get`) let out = JSON.parse(p.stdout) assert.ok(out.length >= 2) assert.ok(out.filter((x: any) => x._id === fid)) - p = new Shell(`${apollo} file get ${P} -i ${fid} ${fid}`) + p = new Shell(`${apollo} file get -i ${fid} ${fid}`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) - p = new Shell(`${apollo} file get ${P} -i nonexists`) + p = new Shell(`${apollo} file get -i nonexists`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 0) }) void globalThis.itName('Download file', () => { - let p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta`) + let p = new Shell(`${apollo} file upload test_data/tiny.fasta`) const up = JSON.parse(p.stdout) if (fs.existsSync(up.basename)) { throw new Error( @@ -1516,68 +1477,68 @@ void describe('Test CLI', () => { ) } - new Shell(`${apollo} file download ${P} -i ${up._id}`) + new Shell(`${apollo} file download -i ${up._id}`) let down = fs.readFileSync(up.basename).toString() assert.ok(down.startsWith('>')) assert.ok(down.trim().endsWith('accc')) fs.unlinkSync(up.basename) - new Shell(`${apollo} file download ${P} -i ${up._id} -o tmp.fa`) + new Shell(`${apollo} file download -i ${up._id} -o tmp.fa`) down = fs.readFileSync('tmp.fa').toString() assert.ok(down.startsWith('>')) assert.ok(down.trim().endsWith('accc')) fs.unlinkSync('tmp.fa') - p = new Shell(`${apollo} file download ${P} -i ${up._id} -o -`) + p = new Shell(`${apollo} file download -i ${up._id} -o -`) assert.ok(p.stdout.startsWith('>')) assert.ok(p.stdout.trim().endsWith('accc')) }) void globalThis.itName('Delete file', () => { - let p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta`) + let p = new Shell(`${apollo} file upload test_data/tiny.fasta`) const up1 = JSON.parse(p.stdout) - p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta`) + p = new Shell(`${apollo} file upload test_data/tiny.fasta`) const up2 = JSON.parse(p.stdout) - p = new Shell(`${apollo} file delete ${P} -i ${up1._id} ${up2._id}`) + p = new Shell(`${apollo} file delete -i ${up1._id} ${up2._id}`) let out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) - p = new Shell(`${apollo} file get ${P} -i ${up1._id} ${up2._id}`) + p = new Shell(`${apollo} file get -i ${up1._id} ${up2._id}`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 0) }) void globalThis.itName('Export gff3 from editable assembly', () => { new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta.gz -a vv1 -f --editable`, + `${apollo} assembly add-from-fasta test_data/tiny.fasta.gz -a vv1 -f --editable`, ) - new Shell(`${apollo} feature import ${P} test_data/tiny.fasta.gff3 -a vv1`) - let p = new Shell(`${apollo} export gff3 ${P} vv1 --include-fasta`) + new Shell(`${apollo} feature import test_data/tiny.fasta.gff3 -a vv1`) + let p = new Shell(`${apollo} export gff3 vv1 --include-fasta`) let gff = p.stdout assert.ok(gff.startsWith('##gff-version 3')) assert.ok(gff.includes('multivalue=val1,val2,val3')) assert.ok(gff.includes('##FASTA\n')) assert.deepStrictEqual(gff.slice(-6), 'taccc\n') - p = new Shell(`${apollo} export gff3 ${P} vv1`) + p = new Shell(`${apollo} export gff3 vv1`) gff = p.stdout assert.ok(gff.startsWith('##gff-version 3')) assert.ok(gff.includes('multivalue=val1,val2,val3')) assert.ok(!gff.includes('##FASTA\n')) // Invalid assembly - p = new Shell(`${apollo} export gff3 ${P} foobar`, false) + p = new Shell(`${apollo} export gff3 foobar`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('foobar')) }) void globalThis.itName('Export gff3 from non-editable assembly', () => { new Shell( - `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta.gz -a vv1 -f`, + `${apollo} assembly add-from-fasta test_data/tiny.fasta.gz -a vv1 -f`, ) - new Shell(`${apollo} feature import ${P} test_data/tiny.fasta.gff3 -a vv1`) - let p = new Shell(`${apollo} export gff3 ${P} vv1 --include-fasta`) + new Shell(`${apollo} feature import test_data/tiny.fasta.gff3 -a vv1`) + let p = new Shell(`${apollo} export gff3 vv1 --include-fasta`) let gff = p.stdout assert.ok(gff.startsWith('##gff-version 3')) assert.ok(gff.includes('multivalue=val1,val2,val3')) @@ -1585,7 +1546,7 @@ void describe('Test CLI', () => { // We end with two newlines because the test data does have an extra newline at the end. assert.deepStrictEqual(gff.slice(-7), 'taccc\n\n') - p = new Shell(`${apollo} export gff3 ${P} vv1`) + p = new Shell(`${apollo} export gff3 vv1`) gff = p.stdout assert.ok(gff.startsWith('##gff-version 3')) assert.ok(gff.includes('multivalue=val1,val2,val3')) @@ -1594,10 +1555,10 @@ void describe('Test CLI', () => { void globalThis.itName('Export gff3 from external assembly', () => { new Shell( - `${apollo} assembly add-from-fasta ${P} http://localhost:3131/tiny.fasta.gz -a vv1 -f`, + `${apollo} assembly add-from-fasta http://localhost:3131/tiny.fasta.gz -a vv1 -f`, ) - new Shell(`${apollo} feature import ${P} test_data/tiny.fasta.gff3 -a vv1`) - let p = new Shell(`${apollo} export gff3 ${P} vv1 --include-fasta`) + new Shell(`${apollo} feature import test_data/tiny.fasta.gff3 -a vv1`) + let p = new Shell(`${apollo} export gff3 vv1 --include-fasta`) let gff = p.stdout assert.ok(gff.startsWith('##gff-version 3')) assert.ok(gff.includes('multivalue=val1,val2,val3')) @@ -1605,7 +1566,7 @@ void describe('Test CLI', () => { // We end with two newlines because the test data does have an extra newline at the end. assert.deepStrictEqual(gff.slice(-7), 'taccc\n\n') - p = new Shell(`${apollo} export gff3 ${P} vv1`) + p = new Shell(`${apollo} export gff3 vv1`) gff = p.stdout assert.ok(gff.startsWith('##gff-version 3')) assert.ok(gff.includes('multivalue=val1,val2,val3')) @@ -1616,12 +1577,12 @@ void describe('Test CLI', () => { 'Position of internal stop codon warning in forward', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/warningPositionForward.gff -a vv1 -f`, + `${apollo} assembly add-from-gff test_data/warningPositionForward.gff -a vv1 -f`, ) - deleteAllChecks(apollo, P, 'vv1') - new Shell(`${apollo} assembly check ${P} -a vv1 -c CDSCheck`) + deleteAllChecks(apollo, 'vv1') + new Shell(`${apollo} assembly check -a vv1 -c CDSCheck`) - const p = new Shell(`${apollo} feature check ${P} -a vv1`) + const p = new Shell(`${apollo} feature check -a vv1`) const out = JSON.parse(p.stdout) assert.deepStrictEqual(out.length, 2) @@ -1639,11 +1600,11 @@ void describe('Test CLI', () => { 'Position of internal stop codon warning in reverse', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/warningPositionReverse.gff -a vv1 -f`, + `${apollo} assembly add-from-gff test_data/warningPositionReverse.gff -a vv1 -f`, ) - deleteAllChecks(apollo, P, 'vv1') - new Shell(`${apollo} assembly check ${P} -a vv1 -c CDSCheck`) - const p = new Shell(`${apollo} feature check ${P} -a vv1`) + deleteAllChecks(apollo, 'vv1') + new Shell(`${apollo} assembly check -a vv1 -c CDSCheck`) + const p = new Shell(`${apollo} feature check -a vv1`) const out = JSON.parse(p.stdout) assert.deepStrictEqual(out.length, 2) assert.deepStrictEqual(out.at(0).cause, 'InternalStopCodon') @@ -1658,11 +1619,11 @@ void describe('Test CLI', () => { void globalThis.itName('Detect missing start codon forward', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/missingStartCodonForward.gff3 -a m1 -f`, + `${apollo} assembly add-from-gff test_data/missingStartCodonForward.gff3 -a m1 -f`, ) - deleteAllChecks(apollo, P, 'm1') - new Shell(`${apollo} assembly check ${P} -a m1 -c CDSCheck`) - const p = new Shell(`${apollo} feature check ${P} -a m1`) + deleteAllChecks(apollo, 'm1') + new Shell(`${apollo} assembly check -a m1 -c CDSCheck`) + const p = new Shell(`${apollo} feature check -a m1`) const out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) assert.deepStrictEqual(out.at(0).cause, 'MissingStartCodon') @@ -1673,11 +1634,11 @@ void describe('Test CLI', () => { void globalThis.itName('Detect missing start codon reverse', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/missingStartCodonReverse.gff3 -a m1 -f`, + `${apollo} assembly add-from-gff test_data/missingStartCodonReverse.gff3 -a m1 -f`, ) - deleteAllChecks(apollo, P, 'm1') - new Shell(`${apollo} assembly check ${P} -a m1 -c CDSCheck`) - const p = new Shell(`${apollo} feature check ${P} -a m1`) + deleteAllChecks(apollo, 'm1') + new Shell(`${apollo} assembly check -a m1 -c CDSCheck`) + const p = new Shell(`${apollo} feature check -a m1`) const out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) assert.deepStrictEqual(out.at(0).cause, 'MissingStartCodon') @@ -1688,10 +1649,10 @@ void describe('Test CLI', () => { void globalThis.itName('Edit exon inferred from CDS', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/cdsWithoutExon.gff3 -f`, + `${apollo} assembly add-from-gff test_data/cdsWithoutExon.gff3 -f`, ) let p = new Shell( - `${apollo} feature search ${P} -t mrna01 -a cdsWithoutExon.gff3`, + `${apollo} feature search -t mrna01 -a cdsWithoutExon.gff3`, ) let out = JSON.parse(p.stdout) const gene: any = out.at(0) @@ -1702,27 +1663,27 @@ void describe('Test CLI', () => { const exon_id = exon[0]._id // Before edit - p = new Shell(`${apollo} feature get-id ${P} -i ${exon_id}`) + p = new Shell(`${apollo} feature get-id -i ${exon_id}`) out = JSON.parse(p.stdout) as AnnotationFeature[] assert.deepStrictEqual(out.at(0)?.max, 20) // After edit - new Shell(`${apollo} feature edit-coords ${P} -i ${exon_id} -e 30`) - p = new Shell(`${apollo} feature get-id ${P} -i ${exon_id}`) + new Shell(`${apollo} feature edit-coords -i ${exon_id} -e 30`) + p = new Shell(`${apollo} feature get-id -i ${exon_id}`) out = JSON.parse(p.stdout) as AnnotationFeature[] assert.deepStrictEqual(out.at(0)?.max, 30) }) void globalThis.itName('Check splice site', () => { new Shell( - `${apollo} assembly add-from-gff ${P} test_data/checkSplice.fasta.gff3 -f`, + `${apollo} assembly add-from-gff test_data/checkSplice.fasta.gff3 -f`, ) - deleteAllChecks(apollo, P, 'checkSplice.fasta.gff3') + deleteAllChecks(apollo, 'checkSplice.fasta.gff3') new Shell( - `${apollo} assembly check ${P} -a checkSplice.fasta.gff3 -c TranscriptCheck`, + `${apollo} assembly check -a checkSplice.fasta.gff3 -c TranscriptCheck`, ) - let p = new Shell(`${apollo} feature get ${P} -a checkSplice.fasta.gff3`) + let p = new Shell(`${apollo} feature get -a checkSplice.fasta.gff3`) const features = JSON.parse(p.stdout) const okMrnaId = [] @@ -1757,14 +1718,14 @@ void describe('Test CLI', () => { } p = new Shell( - `${apollo} feature check ${P} -a checkSplice.fasta.gff3 -i ${okMrnaId.join(' ')}`, + `${apollo} feature check -a checkSplice.fasta.gff3 -i ${okMrnaId.join(' ')}`, ) let out = JSON.parse(p.stdout) assert.deepStrictEqual(out, []) // Check forward transcript p = new Shell( - `${apollo} feature check ${P} -a checkSplice.fasta.gff3 -i ${warnMrnaIdForw}`, + `${apollo} feature check -a checkSplice.fasta.gff3 -i ${warnMrnaIdForw}`, ) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 4) @@ -1791,7 +1752,7 @@ void describe('Test CLI', () => { // Check reverse transcript p = new Shell( - `${apollo} feature check ${P} -a checkSplice.fasta.gff3 -i ${warnMrnaIdRev}`, + `${apollo} feature check -a checkSplice.fasta.gff3 -i ${warnMrnaIdRev}`, ) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 4) diff --git a/packages/apollo-cli/src/test/utils.ts b/packages/apollo-cli/src/test/utils.ts index 2c3bf37c0..efd11e5d2 100644 --- a/packages/apollo-cli/src/test/utils.ts +++ b/packages/apollo-cli/src/test/utils.ts @@ -26,7 +26,11 @@ export class Shell { constructor(cmd: string, strict = true, timeout?: number) { process.stdout.write(`${cmd}\n`) cmd = `set -e; set -u; set -o pipefail\n${cmd}` - const p = spawn.sync(cmd, { shell: '/bin/bash', timeout }) + const p = spawn.sync(cmd, { + shell: '/bin/bash', + timeout, + env: { APOLLO_PROFILE: 'testAdmin' }, + }) this.returncode = p.status this.stdout = p.stdout.toString() this.stderr = p.stderr.toString() @@ -38,15 +42,11 @@ export class Shell { } } -export function deleteAllChecks( - apollo: string, - profile: string, - assembly: string, -) { - const p = new Shell(`${apollo} assembly check ${profile}`) +export function deleteAllChecks(apollo: string, assembly: string) { + const p = new Shell(`${apollo} assembly check`) const out = JSON.parse(p.stdout) as CheckResultSnapshot[] const checks = out.map((chk) => chk.name) new Shell( - `${apollo} assembly check ${profile} --assembly ${assembly} --delete --check ${checks.join(' ')}`, + `${apollo} assembly check --assembly ${assembly} --delete --check ${checks.join(' ')}`, ) } From 32046dde7f201aba54ab7d07c6600e256b72a666 Mon Sep 17 00:00:00 2001 From: Garrett Stevens Date: Fri, 12 Dec 2025 23:25:59 +0000 Subject: [PATCH 2/4] Add `feature add` CLI command with tests --- packages/apollo-cli/README.md | 57 +++ .../apollo-cli/src/commands/feature/add.ts | 484 ++++++++++++++++++ packages/apollo-cli/src/test/test.ts | 98 +++- .../src/refSeqs/refSeqs.controller.ts | 7 +- packages/website/docs/admin/cli/feature.md | 57 +++ 5 files changed, 701 insertions(+), 2 deletions(-) create mode 100644 packages/apollo-cli/src/commands/feature/add.ts diff --git a/packages/apollo-cli/README.md b/packages/apollo-cli/README.md index 9ff839be5..648f4a375 100644 --- a/packages/apollo-cli/README.md +++ b/packages/apollo-cli/README.md @@ -38,6 +38,7 @@ USAGE - [`apollo change get`](#apollo-change-get) - [`apollo config [KEY] [VALUE]`](#apollo-config-key-value) - [`apollo export gff3 ASSEMBLY`](#apollo-export-gff3-assembly) +- [`apollo feature add [FEATURE-JSON]`](#apollo-feature-add-feature-json) - [`apollo feature add-child`](#apollo-feature-add-child) - [`apollo feature check`](#apollo-feature-check) - [`apollo feature copy`](#apollo-feature-copy) @@ -385,6 +386,62 @@ EXAMPLES _See code: [src/commands/export/gff3.ts](https://github.com/GMOD/Apollo3/blob/v0.3.10/packages/apollo-cli/src/commands/export/gff3.ts)_ +## `apollo feature add [FEATURE-JSON]` + +Add one or more features to Apollo + +``` +USAGE + $ apollo feature add [FEATURE-JSON] [--profile ] [--config-file ] [-a ] [-r -s + -e -t ] [-F ] + +ARGUMENTS + FEATURE-JSON Inline JSON describing the feature(s) to add. Can also be provided via stdin. + +FLAGS + -F, --feature-json-file= File with JSON describing the feature(s) to add + -a, --assembly= Name or ID of target assembly. Not required if refseq is unique in the database + -e, --max= End position in target reference sequence + -r, --refSeq= Name or ID of target reference sequence + -s, --min= Start position in target reference sequence + -t, --type= Type of child feature + --config-file= Use this config file (mostly for testing) + --profile= Use credentials from this profile + +DESCRIPTION + Add one or more features to Apollo + + A single simple feature can be added using the --min, --max, etc. flags. + + To add multiple features, features with more details, or features with children, you can pass in JSON via argument or + stdin or use the --feature-json-file options. + + +EXAMPLES + Add a single feature by specifying its location and type + + $ apollo feature add --assembly hg19 --refSeq chr3 --min 1000 --max 5000 --type remark + + Add a single feature from inline JSON + + $ apollo feature add \ + '{"assembly":"","refseq":"","min":1,"max":100,"type":""}' + + Add mutilple features from stdin JSON + + echo '[{"assembly":"","refseq":"","min":1,"max":100,"type":""},{" \ + assembly":"","refseq":"","min":101,"max":200,"type":""}]' | \ + apollo feature add + + Add a feature with children from inline JSON + + $ apollo feature add '{"assembly":"","refseq":"","min":1,"max":100,"type":"","children":[{"min":1,"max":50,"type":""}]}' +``` + +_See code: +[src/commands/feature/add.ts](https://github.com/GMOD/Apollo3/blob/v0.3.10/packages/apollo-cli/src/commands/feature/add.ts)_ + ## `apollo feature add-child` Add a child feature (e.g. add an exon to an mRNA) diff --git a/packages/apollo-cli/src/commands/feature/add.ts b/packages/apollo-cli/src/commands/feature/add.ts new file mode 100644 index 000000000..9d9c06679 --- /dev/null +++ b/packages/apollo-cli/src/commands/feature/add.ts @@ -0,0 +1,484 @@ +import { readFile } from 'node:fs/promises' + +import { type AnnotationFeatureSnapshot } from '@apollo-annotation/mst' +import { + type AddFeatureChangeDetails, + type SerializedAddFeatureChange, +} from '@apollo-annotation/shared' +import { Args, Flags } from '@oclif/core' +import { ObjectId } from 'bson' +import { type Response, fetch } from 'undici' + +import { BaseCommand } from '../../baseCommand.js' +import { createFetchErrorMessage, localhostToAddress } from '../../utils.js' + +interface BaseFeatureJSON { + min: number + max: number + type: string + children?: BaseFeatureJSON[] + [key: string]: unknown +} + +interface FeatureJSON extends BaseFeatureJSON { + assembly?: string + refSeq: string +} + +export default class Add extends BaseCommand { + static summary = 'Add one or more features to Apollo' + static description = `A single simple feature can be added using the --min, --max, etc. flags. + +To add multiple features, features with more details, or features with children, you can pass in JSON via argument or stdin or use the --feature-json-file options. +` + + static examples = [ + { + description: 'Add a single feature by specifying its location and type', + command: + '<%= config.bin %> <%= command.id %> --assembly hg19 --refSeq chr3 --min 1000 --max 5000 --type remark', + }, + { + description: 'Add a single feature from inline JSON', + command: + '<%= config.bin %> <%= command.id %> \'{"assembly":"","refseq":"","min":1,"max":100,"type":""}\'', + }, + { + description: 'Add mutilple features from stdin JSON', + command: + 'echo \'[{"assembly":"","refseq":"","min":1,"max":100,"type":""},{"assembly":"","refseq":"","min":101,"max":200,"type":""}]\' | <%= config.bin %> <%= command.id %>', + }, + { + description: 'Add a feature with children from inline JSON', + command: + '<%= config.bin %> <%= command.id %> \'{"assembly":"","refseq":"","min":1,"max":100,"type":"","children":[{"min":1,"max":50,"type":""}]}\'', + }, + ] + + static flags = { + assembly: Flags.string({ + char: 'a', + description: + 'Name or ID of target assembly. Not required if refseq is unique in the database', + }), + refSeq: Flags.string({ + char: 'r', + description: 'Name or ID of target reference sequence', + dependsOn: ['min', 'max', 'type'], + exclusive: ['feature-json-file'], + }), + min: Flags.integer({ + char: 's', + description: 'Start position in target reference sequence', + dependsOn: ['refSeq', 'max', 'type'], + exclusive: ['feature-json-file'], + }), + max: Flags.integer({ + char: 'e', + description: 'End position in target reference sequence', + dependsOn: ['refSeq', 'min', 'type'], + exclusive: ['feature-json-file'], + }), + type: Flags.string({ + char: 't', + description: 'Type of child feature', + dependsOn: ['refSeq', 'min', 'max'], + exclusive: ['feature-json-file'], + }), + 'feature-json-file': Flags.file({ + char: 'F', + description: 'File with JSON describing the feature(s) to add', + exists: true, + }), + } + + static args = { + 'feature-json': Args.string({ + description: + 'Inline JSON describing the feature(s) to add. Can also be provided via stdin.', + }), + } + + public async run(): Promise { + const { args, flags } = this + const { 'feature-json': featureJSONString } = args + const { + assembly, + refSeq, + min, + max, + type, + 'feature-json-file': featureJSONFile, + } = flags + if (featureJSONString) { + if ( + assembly !== undefined || + refSeq !== undefined || + min !== undefined || + max !== undefined || + type !== undefined || + featureJSONFile !== undefined + ) { + this.error( + 'Cannot use the following flags when providing a feature JSON: --assembly, --refSeq, --min, --max, --type, --feature-json-file', + ) + } + await this.addFeatureFromJSON(featureJSONString) + return + } + if (featureJSONFile) { + const fileText = await readFile(featureJSONFile, 'utf8') + await this.addFeatureFromJSON(fileText) + return + } + if ( + refSeq === undefined || + min === undefined || + max === undefined || + type === undefined + ) { + this.error('Must provide all of: --refSeq, --min, --max, and --type') + } + await this.addFeatureFromFlags(refSeq, min, max, type, assembly) + } + + async addFeatureFromJSON(featureJSONString: string) { + let featureJSON: FeatureJSON | FeatureJSON[] + try { + featureJSON = parseFeatureJSON(featureJSONString) + } catch (error) { + this.logToStderr('Error: feature JSON is not valid') + if (error instanceof Error || typeof error === 'string') { + this.error(error) + } + throw error + } + if (Array.isArray(featureJSON)) { + const firstFeature = featureJSON.at(0) + if (!firstFeature) { + throw new Error('Feature array is empty') + } + const { assembly, refSeq } = firstFeature + if (!featureJSON.every((feature) => feature.assembly === assembly)) { + throw new Error( + 'Cannot add features to multiple assemblies at the same time', + ) + } + const [assemblyId] = await this.getAssemblyAndRefSeqIds(refSeq, assembly) + const changedIds: string[] = [] + const changes = await Promise.all( + featureJSON.map( + async (singleFeatureJSON): Promise => { + const { children, assembly, refSeq, ...rest } = singleFeatureJSON + const refSeqDocument = await this.getRefSeq(refSeq) + return { + addedFeature: this.makeFeatureSnapshot( + { + assembly: assemblyId, + refSeq: refSeqDocument._id, + ...rest, + }, + changedIds, + ), + } + }, + ), + ) + const change: SerializedAddFeatureChange = { + changedIds, + typeName: 'AddFeatureChange', + assembly: assemblyId, + changes, + } + return this.submitChange(change) + } + const { assembly, refSeq, ...rest } = featureJSON + const [assemblyId, refSeqId] = await this.getAssemblyAndRefSeqIds( + refSeq, + assembly, + ) + const changedIds: string[] = [] + const details = { + addedFeature: this.makeFeatureSnapshot( + { + assembly: assemblyId, + refSeq: refSeqId, + ...rest, + }, + changedIds, + ), + } + const change: SerializedAddFeatureChange = { + changedIds, + typeName: 'AddFeatureChange', + assembly: assemblyId, + ...details, + } + return this.submitChange(change) + } + + makeFeatureSnapshot( + details: FeatureJSON, + ids: string[], + ): AnnotationFeatureSnapshot { + const { children, ...rest } = details + let childrenDetails: undefined | Record = + undefined + if (children) { + childrenDetails = {} + const { refSeq } = rest + for (const child of children) { + const childDetails = this.makeFeatureSnapshot({ refSeq, ...child }, ids) + childrenDetails[childDetails._id] = childDetails + } + } + const _id = new ObjectId().toHexString() + ids.push(_id) + return { + _id, + ...rest, + ...(childrenDetails ? { children: childrenDetails } : {}), + } + } + + async addFeatureFromFlags( + refSeqNameOrId: string, + min: number, + max: number, + type: string, + assemblyNameOrId?: string, + ) { + const [assemblyId, refSeqId] = await this.getAssemblyAndRefSeqIds( + refSeqNameOrId, + assemblyNameOrId, + ) + const changedIds: string[] = [] + const details = { + addedFeature: this.makeFeatureSnapshot( + { refSeq: refSeqId, min: min - 1, max, type }, + changedIds, + ), + } + const change: SerializedAddFeatureChange = { + changedIds, + typeName: 'AddFeatureChange', + assembly: assemblyId, + ...details, + } + return this.submitChange(change) + } + + async getAssemblyAndRefSeqIds( + refSeqNameOrId: string, + assemblyNameOrId?: string, + ): Promise<[string, string]> { + const refSeqIsObjectId = ObjectId.isValid(refSeqNameOrId) + const assemblyIsObjectId = assemblyNameOrId + ? ObjectId.isValid(assemblyNameOrId) + : false + if (assemblyNameOrId && assemblyIsObjectId && refSeqIsObjectId) { + return [assemblyNameOrId, refSeqNameOrId] + } + if (refSeqIsObjectId) { + if (assemblyNameOrId) { + this.warn('Ignoring provided --assembly because it is not an ID') + } + const refSeqDocument = await this.getRefSeq(refSeqNameOrId) + return [refSeqDocument.assembly, refSeqNameOrId] + } + if (!assemblyNameOrId) { + this.error( + `If provided refSeq (${refSeqNameOrId}) is not an ID, assembly must also be provided`, + ) + } + const assemblyDocument = await this.getAssembly(assemblyNameOrId) + const refSeqDocument = await this.getRefSeq( + refSeqNameOrId, + assemblyDocument._id, + ) + return [assemblyDocument._id, refSeqDocument._id] + } + + async getAssembly( + assemblyNameOrId: string, + ): Promise<{ _id: string; name: string; aliases?: string[] }> { + if (ObjectId.isValid(assemblyNameOrId)) { + const response = await this.fetch(`assemblies/${assemblyNameOrId}`) + if (!response.ok) { + const errorMessage = await createFetchErrorMessage( + response, + `Could not find assembly: "${assemblyNameOrId}"`, + ) + this.error(errorMessage) + } + return response.json() as Promise<{ + _id: string + name: string + aliases?: string[] + }> + } + const response = await this.fetch('assemblies') + if (!response.ok) { + const errorMessage = await createFetchErrorMessage( + response, + `Could not find assembly: "${assemblyNameOrId}"`, + ) + this.error(errorMessage) + } + const assemblies = (await response.json()) as { + _id: string + name: string + aliases?: string[] + }[] + for (const assembly of assemblies) { + if ( + assembly.name === assemblyNameOrId || + assembly.aliases?.includes(assemblyNameOrId) + ) { + return assembly + } + } + throw new Error(`Could not find assembly: "${assemblyNameOrId}"`) + } + + async getRefSeq( + refSeqNameOrId: string, + assemblyId?: string, + ): Promise<{ + _id: string + name: string + assembly: string + aliases?: string[] + }> { + if (ObjectId.isValid(refSeqNameOrId)) { + const response = await this.fetch(`refSeqs/${refSeqNameOrId}`) + if (!response.ok) { + const errorMessage = await createFetchErrorMessage( + response, + `Could not find refSeq: "${refSeqNameOrId}"`, + ) + this.error(errorMessage) + } + return response.json() as Promise<{ + _id: string + name: string + assembly: string + aliases?: string[] + }> + } + let endpoint = 'refSeqs' + if (assemblyId) { + const searchParams = new URLSearchParams({ assembly: assemblyId }) + endpoint = `${endpoint}?${searchParams.toString()}` + } + const response = await this.fetch(endpoint) + if (!response.ok) { + const errorMessage = await createFetchErrorMessage( + response, + `Could not find refSeq: "${refSeqNameOrId}"`, + ) + this.error(errorMessage) + } + const refSeqs = (await response.json()) as { + _id: string + name: string + assembly: string + aliases?: string[] + }[] + for (const refSeq of refSeqs) { + if ( + refSeq.name === refSeqNameOrId || + refSeq.aliases?.includes(refSeqNameOrId) + ) { + return refSeq + } + } + throw new Error(`Could not find refSeq: "${refSeqNameOrId}"`) + } + + async submitChange(change: SerializedAddFeatureChange) { + const options = { method: 'POST', body: JSON.stringify(change) } + const response = await this.fetch('changes', options) + if (!response.ok) { + const errorMessage = await createFetchErrorMessage( + response, + 'Could not add feature', + ) + this.error(errorMessage) + } + this.log(await response.text()) + } + + async fetch( + endpoint: string, + options?: { + method?: string + body?: string + headers?: Record + }, + ): Promise { + const { address, accessToken } = await this.getAccess() + const url = new URL(localhostToAddress(`${address}/${endpoint}`)) + const optionsWithAuth = { + ...options, + headers: { + authorization: `Bearer ${accessToken}`, + 'Content-Type': 'application/json', + ...options?.headers, + }, + } + return fetch(url, optionsWithAuth) + } +} + +function parseFeatureJSON( + featureJSONString: string, +): FeatureJSON | FeatureJSON[] { + const featureJSON: unknown = JSON.parse(featureJSONString) + if (Array.isArray(featureJSON)) { + return featureJSON.map((feature) => { + assertFeatureIsValid(feature, true) + return feature + }) + } + assertFeatureIsValid(featureJSON, true) + return featureJSON +} + +function assertFeatureIsValid( + feature: unknown, + topLevel = false, +): asserts feature is FeatureJSON { + if ( + typeof feature !== 'object' || + feature === null || + Array.isArray(feature) + ) { + throw new TypeError( + `Feature is not a key-value record: '${JSON.stringify(feature)}'`, + ) + } + for (const attribute of ['min', 'max', 'type']) { + if (!(attribute in feature)) { + throw new Error( + `Feature does not contain "${attribute}": '${JSON.stringify(feature)}'`, + ) + } + } + if (topLevel && !('refSeq' in feature)) { + throw new Error( + `Top-level feature does not contain "refSeq": '${JSON.stringify(feature)}'`, + ) + } + if ('children' in feature) { + const { children } = feature + if (!Array.isArray(children)) { + throw new TypeError( + `"children" is not an array of features '${JSON.stringify(feature)}'`, + ) + } + for (const child of children) { + assertFeatureIsValid(child) + } + } +} diff --git a/packages/apollo-cli/src/test/test.ts b/packages/apollo-cli/src/test/test.ts index ee62d1aee..0b91048fe 100644 --- a/packages/apollo-cli/src/test/test.ts +++ b/packages/apollo-cli/src/test/test.ts @@ -32,7 +32,6 @@ import { MongoClient } from 'mongodb' import { Shell, deleteAllChecks } from './utils.js' const apollo = 'yarn dev' -// let client = MongoClient let client: MongoClient let configFile = '' let configFileBak = '' @@ -841,6 +840,103 @@ void describe('Test CLI', () => { assert.strictEqual(p.returncode, 0) }) + void globalThis.itName('Add features', () => { + let p = new Shell( + `${apollo} assembly add-from-fasta test_data/tiny.fasta.gz -a tiny -f`, + ) + let out = JSON.parse(p.stdout) + const assemblyId = out._id + p = new Shell(`${apollo} feature get -a tiny`) + assert.deepStrictEqual(p.stdout.trim(), '[]') + // Can add a feature using flags + p = new Shell(`${apollo} feature add -a tiny -r ctgA -s 1 -e 10 -t remark`) + out = JSON.parse(p.stdout) + p = new Shell(`${apollo} feature get -a tiny`) + out = JSON.parse(p.stdout) + assert.strictEqual(out.length, 1) + const refSeqId = out[0].refSeq + // Can add a feature using assembly and refSeq ids + p = new Shell( + `${apollo} feature add -a ${assemblyId} -r ${refSeqId} -s 11 -e 20 -t remark`, + ) + p = new Shell(`${apollo} feature get -a ${assemblyId}`) + out = JSON.parse(p.stdout) + assert.strictEqual(out.length, 2) + // Can add a feature using JSON arg + p = new Shell( + `${apollo} feature add '{"assembly":"${assemblyId}","refSeq":"${refSeqId}","min":21,"max":30,"type":"remark"}'`, + ) + p = new Shell(`${apollo} feature get -a ${assemblyId}`) + out = JSON.parse(p.stdout) + assert.strictEqual(out.length, 3) + // Can add a feature using JSON from stdin + p = new Shell( + `${apollo} feature add < { new Shell( `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, diff --git a/packages/apollo-collaboration-server/src/refSeqs/refSeqs.controller.ts b/packages/apollo-collaboration-server/src/refSeqs/refSeqs.controller.ts index b73b48bc7..a549d1f15 100644 --- a/packages/apollo-collaboration-server/src/refSeqs/refSeqs.controller.ts +++ b/packages/apollo-collaboration-server/src/refSeqs/refSeqs.controller.ts @@ -1,4 +1,4 @@ -import { Controller, Get, Logger, Query } from '@nestjs/common' +import { Controller, Get, Logger, Param, Query } from '@nestjs/common' import { Role } from '../utils/role/role.enum' import { Validations } from '../utils/validation/validatation.decorator' @@ -17,4 +17,9 @@ export class RefSeqsController { findAll(@Query() request: FindRefSeqDto) { return this.refSeqsService.findAll(request) } + + @Get(':refseqid') + getFeature(@Param('refseqid') refseqid: string) { + return this.refSeqsService.findOne(refseqid) + } } diff --git a/packages/website/docs/admin/cli/feature.md b/packages/website/docs/admin/cli/feature.md index e1e0063b8..7f689ca06 100644 --- a/packages/website/docs/admin/cli/feature.md +++ b/packages/website/docs/admin/cli/feature.md @@ -2,6 +2,7 @@ Commands to manage features +- [`apollo feature add [FEATURE-JSON]`](#apollo-feature-add-feature-json) - [`apollo feature add-child`](#apollo-feature-add-child) - [`apollo feature check`](#apollo-feature-check) - [`apollo feature copy`](#apollo-feature-copy) @@ -16,6 +17,62 @@ Commands to manage features - [`apollo feature import INPUT-FILE`](#apollo-feature-import-input-file) - [`apollo feature search`](#apollo-feature-search) +## `apollo feature add [FEATURE-JSON]` + +Add one or more features to Apollo + +``` +USAGE + $ apollo feature add [FEATURE-JSON] [--profile ] [--config-file ] [-a ] [-r -s + -e -t ] [-F ] + +ARGUMENTS + FEATURE-JSON Inline JSON describing the feature(s) to add. Can also be provided via stdin. + +FLAGS + -F, --feature-json-file= File with JSON describing the feature(s) to add + -a, --assembly= Name or ID of target assembly. Not required if refseq is unique in the database + -e, --max= End position in target reference sequence + -r, --refSeq= Name or ID of target reference sequence + -s, --min= Start position in target reference sequence + -t, --type= Type of child feature + --config-file= Use this config file (mostly for testing) + --profile= Use credentials from this profile + +DESCRIPTION + Add one or more features to Apollo + + A single simple feature can be added using the --min, --max, etc. flags. + + To add multiple features, features with more details, or features with children, you can pass in JSON via argument or + stdin or use the --feature-json-file options. + + +EXAMPLES + Add a single feature by specifying its location and type + + $ apollo feature add --assembly hg19 --refSeq chr3 --min 1000 --max 5000 --type remark + + Add a single feature from inline JSON + + $ apollo feature add \ + '{"assembly":"","refseq":"","min":1,"max":100,"type":""}' + + Add mutilple features from stdin JSON + + echo '[{"assembly":"","refseq":"","min":1,"max":100,"type":""},{" \ + assembly":"","refseq":"","min":101,"max":200,"type":""}]' | \ + apollo feature add + + Add a feature with children from inline JSON + + $ apollo feature add '{"assembly":"","refseq":"","min":1,"max":100,"type":"","children":[{"min":1,"max":50,"type":""}]}' +``` + +_See code: +[src/commands/feature/add.ts](https://github.com/GMOD/Apollo3/blob/v0.3.10/packages/apollo-cli/src/commands/feature/add.ts)_ + ## `apollo feature add-child` Add a child feature (e.g. add an exon to an mRNA) From ed3e5ae7fe929e2fa5dcebdec5ad68d706a9a6fb Mon Sep 17 00:00:00 2001 From: Garrett Stevens Date: Tue, 16 Dec 2025 01:03:01 +0000 Subject: [PATCH 3/4] Revert test simplification --- packages/apollo-cli/src/test/test.ts | 744 ++++++++++++++------------ packages/apollo-cli/src/test/utils.ts | 16 +- 2 files changed, 401 insertions(+), 359 deletions(-) diff --git a/packages/apollo-cli/src/test/test.ts b/packages/apollo-cli/src/test/test.ts index 0b91048fe..76704f8d3 100644 --- a/packages/apollo-cli/src/test/test.ts +++ b/packages/apollo-cli/src/test/test.ts @@ -32,6 +32,8 @@ import { MongoClient } from 'mongodb' import { Shell, deleteAllChecks } from './utils.js' const apollo = 'yarn dev' +const P = '--profile testAdmin' +// let client = MongoClient let client: MongoClient let configFile = '' let configFileBak = '' @@ -48,10 +50,10 @@ void describe('Test CLI', () => { `Backup config file ${configFileBak} already exists. If safe to do so, delete it before testing`, ) } - new Shell(`${apollo} config address http://localhost:3999`) - new Shell(`${apollo} config accessType root`) - new Shell(`${apollo} config rootPassword pass`) - new Shell(`${apollo} login -f`) + new Shell(`${apollo} config ${P} address http://localhost:3999`) + new Shell(`${apollo} config ${P} accessType root`) + new Shell(`${apollo} config ${P} rootPassword pass`) + new Shell(`${apollo} login ${P} -f`) }) after(async () => { @@ -93,95 +95,95 @@ void describe('Test CLI', () => { }) void globalThis.itName('Config invalid keys', () => { - let p = new Shell(`${apollo} config address spam`, false) + let p = new Shell(`${apollo} config ${P} address spam`, false) assert.strictEqual(1, p.returncode) assert.ok(p.stderr.includes('Invalid setting:')) - p = new Shell(`${apollo} config ADDRESS http://localhost:3999`, false) + p = new Shell(`${apollo} config ${P} ADDRESS http://localhost:3999`, false) assert.strictEqual(1, p.returncode) assert.ok(p.stderr.includes('Invalid setting:')) - p = new Shell(`${apollo} config accessType spam`, false) + p = new Shell(`${apollo} config ${P} accessType spam`, false) assert.strictEqual(1, p.returncode) assert.ok(p.stderr.includes('Invalid setting:')) }) void globalThis.itName('Can change access type', () => { - const p = new Shell(`${apollo} config accessType google`) + const p = new Shell(`${apollo} config ${P} accessType google`) assert.strictEqual('', p.stdout.trim()) }) void globalThis.itName('Apollo status', () => { - let p = new Shell(`${apollo} status`) + let p = new Shell(`${apollo} status ${P}`) assert.strictEqual(p.stdout.trim(), 'testAdmin: Logged in') - new Shell(`${apollo} logout`) - p = new Shell(`${apollo} status`) + new Shell(`${apollo} logout ${P}`) + p = new Shell(`${apollo} status ${P}`) assert.strictEqual(p.stdout.trim(), 'testAdmin: Logged out') - new Shell(`${apollo} login -f`) + new Shell(`${apollo} login ${P} -f`) }) void globalThis.itName('Feature get', () => { new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 -f`, ) new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv2 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv2 -f`, ) - let p = new Shell(`${apollo} feature get -a vv1`) + let p = new Shell(`${apollo} feature get ${P} -a vv1`) assert.ok(p.stdout.includes('ctgA')) assert.ok(p.stdout.includes('SomeContig')) - p = new Shell(`${apollo} feature get -r ctgA`, false) + p = new Shell(`${apollo} feature get ${P} -r ctgA`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('found in more than one assembly')) - p = new Shell(`${apollo} feature get -a vv1 -r ctgA`) + p = new Shell(`${apollo} feature get ${P} -a vv1 -r ctgA`) let out = JSON.parse(p.stdout) assert.ok(Object.keys(out.at(0)).length > 2) - p = new Shell(`${apollo} feature get -a vv1 -r ctgA -s 40 -e 41`) + p = new Shell(`${apollo} feature get ${P} -a vv1 -r ctgA -s 40 -e 41`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) - p = new Shell(`${apollo} feature get -a vv1 -r ctgA -s 1000 -e 1000`) + p = new Shell(`${apollo} feature get ${P} -a vv1 -r ctgA -s 1000 -e 1000`) out = JSON.parse(p.stdout) assert.deepStrictEqual(out, []) - p = new Shell(`${apollo} feature get -r FOOBAR`) + p = new Shell(`${apollo} feature get ${P} -r FOOBAR`) out = JSON.parse(p.stdout) assert.deepStrictEqual(out, []) - p = new Shell(`${apollo} feature get -a FOOBAR -r ctgA`, false) + p = new Shell(`${apollo} feature get ${P} -a FOOBAR -r ctgA`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('returned 0 assemblies')) }) void globalThis.itName('Assembly get', () => { new Shell( - `${apollo} assembly add-from-fasta test_data/tiny.fasta -a vv1 -e -f`, + `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a vv1 -e -f`, ) new Shell( - `${apollo} assembly add-from-fasta test_data/tiny.fasta -a vv2 -e -f`, + `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a vv2 -e -f`, ) new Shell( - `${apollo} assembly add-from-fasta test_data/tiny.fasta -a vv3 -e -f`, + `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a vv3 -e -f`, ) - let p = new Shell(`${apollo} assembly get`) + let p = new Shell(`${apollo} assembly get ${P}`) assert.ok(p.stdout.includes('vv1')) assert.ok(p.stdout.includes('vv2')) assert.ok(p.stdout.includes('vv3')) - p = new Shell(`${apollo} assembly get -a vv1 vv2`) + p = new Shell(`${apollo} assembly get ${P} -a vv1 vv2`) assert.ok(p.stdout.includes('vv1')) assert.ok(p.stdout.includes('vv2')) assert.ok(p.stdout.includes('vv3') == false) const out = JSON.parse(p.stdout) const aid = out.find((x: any) => x.name === 'vv1')._id - p = new Shell(`${apollo} assembly get -a ${aid} vv2`) + p = new Shell(`${apollo} assembly get ${P} -a ${aid} vv2`) assert.ok(p.stdout.includes('vv1')) assert.ok(p.stdout.includes('vv2')) assert.ok(p.stdout.includes('vv3') == false) @@ -189,26 +191,26 @@ void describe('Test CLI', () => { void globalThis.itName('Delete assembly', () => { new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a volvox1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a volvox1 -f`, ) new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a volvox2 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a volvox2 -f`, ) new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a volvox3 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a volvox3 -f`, ) let p = new Shell( - `${apollo} assembly get | jq '.[] | select(.name == "volvox1") | ._id'`, + `${apollo} assembly get ${P} | jq '.[] | select(.name == "volvox1") | ._id'`, ) const aid = p.stdout.trim() - p = new Shell(`${apollo} assembly delete -v -a ${aid} volvox2 volvox2`) + p = new Shell(`${apollo} assembly delete ${P} -v -a ${aid} volvox2 volvox2`) const out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) assert.ok(p.stderr.includes('2 ')) - new Shell(`${apollo} assembly delete -a ${aid} volvox2`) - p = new Shell(`${apollo} assembly get`) + new Shell(`${apollo} assembly delete ${P} -a ${aid} volvox2`) + p = new Shell(`${apollo} assembly get ${P}`) assert.ok(p.stdout.includes(aid) == false) assert.ok(p.stdout.includes('volvox1') == false) assert.ok(p.stdout.includes('volvox2') == false) @@ -217,45 +219,47 @@ void describe('Test CLI', () => { void globalThis.itName('Id reader', () => { new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a v1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a v1 -f`, ) new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a v2 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a v2 -f`, ) new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a v3 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a v3 -f`, ) - let p = new Shell(`${apollo} assembly get`) + let p = new Shell(`${apollo} assembly get ${P}`) const xall = JSON.parse(p.stdout) - p = new Shell(`${apollo} assembly get -a v1 v2`) + p = new Shell(`${apollo} assembly get ${P} -a v1 v2`) let out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) // This is interpreted as an assembly named 'v1 v2' - p = new Shell(`echo v1 v2 | ${apollo} assembly get -a -`) + p = new Shell(`echo v1 v2 | ${apollo} assembly get ${P} -a -`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 0) // These are two assemblies - p = new Shell(`echo -e 'v1 \n v2' | ${apollo} assembly get -a -`) + p = new Shell(`echo -e 'v1 \n v2' | ${apollo} assembly get ${P} -a -`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) - p = new Shell(`${apollo} assembly get | ${apollo} assembly get -a -`) + p = new Shell( + `${apollo} assembly get ${P} | ${apollo} assembly get ${P} -a -`, + ) out = JSON.parse(p.stdout) assert.ok(out.length >= 3) // From json file - new Shell(`${apollo} assembly get > test_data/tmp.json`) - p = new Shell(`${apollo} assembly get -a test_data/tmp.json`) + new Shell(`${apollo} assembly get ${P} > test_data/tmp.json`) + p = new Shell(`${apollo} assembly get ${P} -a test_data/tmp.json`) out = JSON.parse(p.stdout) assert.ok(out.length >= 3) fs.unlinkSync('test_data/tmp.json') // From text file, one name or id per line fs.writeFileSync('test_data/tmp.txt', 'v1 \n v2 \r\n v3 \n') - p = new Shell(`${apollo} assembly get -a test_data/tmp.txt`) + p = new Shell(`${apollo} assembly get ${P} -a test_data/tmp.txt`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 3) fs.unlinkSync('test_data/tmp.txt') @@ -263,7 +267,7 @@ void describe('Test CLI', () => { // From json string const aid = xall.at(0)._id let j = `{"_id": "${aid}"}` - p = new Shell(`${apollo} assembly get -a '${j}'`) + p = new Shell(`${apollo} assembly get ${P} -a '${j}'`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) assert.strictEqual(out.at(0)._id, aid) @@ -271,52 +275,54 @@ void describe('Test CLI', () => { const id1 = xall.at(0)._id const id2 = xall.at(1)._id j = `[{"_id": "${id1}"}, {"_id": "${id2}"}]` - p = new Shell(`${apollo} assembly get -a '${j}'`) + p = new Shell(`${apollo} assembly get ${P} -a '${j}'`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) j = `{"XYZ": "${aid}"}` - p = new Shell(`${apollo} assembly get -a '${j}'`) + p = new Shell(`${apollo} assembly get ${P} -a '${j}'`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 0) - p = new Shell(`${apollo} assembly get -a '[...'`) + p = new Shell(`${apollo} assembly get ${P} -a '[...'`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 0) }) void globalThis.itName('Add assembly from gff', () => { let p = new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 --omit-features -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 --omit-features -f`, ) const out = JSON.parse(p.stdout) assert.ok(Object.keys(out.fileIds).includes('fa')) // Get id of assembly named vv1 and check there are no features - p = new Shell(`${apollo} assembly get -a vv1`) + p = new Shell(`${apollo} assembly get ${P} -a vv1`) assert.ok(p.stdout.includes('vv1')) assert.ok(p.stdout.includes('vv2') == false) const asm_id = JSON.parse(p.stdout).at(0)._id - p = new Shell(`${apollo} refseq get`) + p = new Shell(`${apollo} refseq get ${P}`) const refseq = JSON.parse(p.stdout.trim()) const vv1ref = refseq.filter((x: any) => x.assembly === asm_id) const refseq_id = vv1ref.find((x: any) => x.name === 'ctgA')._id - p = new Shell(`${apollo} feature get -r ${refseq_id}`) + p = new Shell(`${apollo} feature get ${P} -r ${refseq_id}`) const ff = JSON.parse(p.stdout) assert.deepStrictEqual(ff, []) p = new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1`, false, ) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('Error: Assembly "vv1" already exists')) // Default assembly name - new Shell(`${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -f`) - p = new Shell(`${apollo} assembly get -a tiny.fasta.gff3`) + new Shell( + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -f`, + ) + p = new Shell(`${apollo} assembly get ${P} -a tiny.fasta.gff3`) assert.ok(p.stdout.includes('tiny.fasta.gff3')) }) @@ -329,18 +335,18 @@ void describe('Test CLI', () => { stream.close() new Shell( - `${apollo} assembly add-from-fasta test_data/tmp.fa -a test -e -f`, + `${apollo} assembly add-from-fasta ${P} test_data/tmp.fa -a test -e -f`, true, 60_000, ) new Shell( - `${apollo} assembly add-from-gff test_data/tmp.fa -a test -f`, + `${apollo} assembly add-from-gff ${P} test_data/tmp.fa -a test -f`, false, 60_000, ) new Shell( - `${apollo} assembly add-from-fasta test_data/tmp.fa -a test -e -f`, + `${apollo} assembly add-from-fasta ${P} test_data/tmp.fa -a test -e -f`, true, 60_000, ) @@ -349,11 +355,11 @@ void describe('Test CLI', () => { }) void globalThis.itName('Checks are triggered and resolved', () => { - new Shell(`${apollo} assembly add-from-gff test_data/checks.gff -f`) - let p = new Shell(`${apollo} feature get -a checks.gff`) + new Shell(`${apollo} assembly add-from-gff ${P} test_data/checks.gff -f`) + let p = new Shell(`${apollo} feature get ${P} -a checks.gff`) const out = JSON.parse(p.stdout) - p = new Shell(`${apollo} feature check -a checks.gff`) + p = new Shell(`${apollo} feature check ${P} -a checks.gff`) assert.deepStrictEqual(p.stdout.trim(), '[]') // No failing check // Get the ID of the CDS. We need need it to modify the CDS coordinates @@ -368,25 +374,29 @@ void describe('Test CLI', () => { const cds_id = cds._id // Introduce problems - new Shell(`${apollo} feature edit-coords -i ${cds_id} --start 4 --end 24`) - p = new Shell(`${apollo} feature check -a checks.gff`) + new Shell( + `${apollo} feature edit-coords ${P} -i ${cds_id} --start 4 --end 24`, + ) + p = new Shell(`${apollo} feature check ${P} -a checks.gff`) const checks = JSON.parse(p.stdout) assert.strictEqual(checks.length, 2) assert.ok(p.stdout.includes('InternalStopCodon')) assert.ok(p.stdout.includes('MissingStopCodon')) // Problems fixed - new Shell(`${apollo} feature edit-coords -i ${cds_id} --start 16 --end 27`) - p = new Shell(`${apollo} feature check -a checks.gff`) + new Shell( + `${apollo} feature edit-coords ${P} -i ${cds_id} --start 16 --end 27`, + ) + p = new Shell(`${apollo} feature check ${P} -a checks.gff`) assert.deepStrictEqual(JSON.parse(p.stdout).length, 0) }) void globalThis.itName('FIXME: Checks stay after invalid operation', () => { - new Shell(`${apollo} assembly add-from-gff test_data/checks.gff -f`) - let p = new Shell(`${apollo} feature get -a checks.gff`) + new Shell(`${apollo} assembly add-from-gff ${P} test_data/checks.gff -f`) + let p = new Shell(`${apollo} feature get ${P} -a checks.gff`) const out = JSON.parse(p.stdout) - p = new Shell(`${apollo} feature check -a checks.gff`) + p = new Shell(`${apollo} feature check ${P} -a checks.gff`) assert.deepStrictEqual(p.stdout.trim(), '[]') // No failing check // Get the ID of the CDS. We need need it to modify the CDS coordinates @@ -401,20 +411,25 @@ void describe('Test CLI', () => { const cds_id = cds._id // Introduce problems - new Shell(`${apollo} feature edit-coords -i ${cds_id} --start 4 --end 24`) - p = new Shell(`${apollo} feature check -a checks.gff`) + new Shell( + `${apollo} feature edit-coords ${P} -i ${cds_id} --start 4 --end 24`, + ) + p = new Shell(`${apollo} feature check ${P} -a checks.gff`) let checks = JSON.parse(p.stdout) assert.strictEqual(checks.length, 2) assert.ok(p.stdout.includes('InternalStopCodon')) assert.ok(p.stdout.includes('MissingStopCodon')) // Do something invalid: extend CDS beyond parent - p = new Shell(`${apollo} feature edit-coords -i ${cds_id} --end 30`, false) + p = new Shell( + `${apollo} feature edit-coords ${P} -i ${cds_id} --end 30`, + false, + ) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('exceeds the bounds of its parent')) // FIXME: Checks should be the same as before the invalid edit - p = new Shell(`${apollo} feature check -a checks.gff`) + p = new Shell(`${apollo} feature check ${P} -a checks.gff`) checks = JSON.parse(p.stdout) //assert.strictEqual(checks.length, 2) //assert.ok(p.stdout.includes('InternalStopCodon')) @@ -423,46 +438,51 @@ void describe('Test CLI', () => { void globalThis.itName('Add assembly from local fasta', () => { let p = new Shell( - `${apollo} assembly add-from-fasta test_data/tiny.fasta -a vv1 -e -f`, + `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a vv1 -e -f`, ) const out = JSON.parse(p.stdout) assert.ok(Object.keys(out.fileIds).includes('fa')) - p = new Shell(`${apollo} assembly get -a vv1`) + p = new Shell(`${apollo} assembly get ${P} -a vv1`) assert.ok(p.stdout.includes('vv1')) p = new Shell( - `${apollo} assembly add-from-fasta test_data/tiny.fasta -a vv1 -e`, + `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a vv1 -e`, false, ) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('Error: Assembly "vv1" already exists')) - p = new Shell(`${apollo} assembly add-from-fasta na.fa -a vv1 -e -f`, false) + p = new Shell( + `${apollo} assembly add-from-fasta ${P} na.fa -a vv1 -e -f`, + false, + ) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('Input')) // Test default name - new Shell(`${apollo} assembly add-from-fasta test_data/tiny.fasta -e -f`) - p = new Shell(`${apollo} assembly get -a tiny.fasta`) + new Shell( + `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -e -f`, + ) + p = new Shell(`${apollo} assembly get ${P} -a tiny.fasta`) assert.ok(p.stdout.includes('tiny.fasta')) }) void globalThis.itName('Add assembly from external fasta', () => { let p = new Shell( - `${apollo} assembly add-from-fasta -a vv1 -f http://localhost:3131/volvox.fa.gz`, + `${apollo} assembly add-from-fasta ${P} -a vv1 -f http://localhost:3131/volvox.fa.gz`, ) const out = JSON.parse(p.stdout) assert.ok(Object.keys(out.externalLocation).includes('fa')) - p = new Shell(`${apollo} assembly get -a vv1`) + p = new Shell(`${apollo} assembly get ${P} -a vv1`) assert.ok(p.stdout.includes('vv1')) - p = new Shell(`${apollo} assembly sequence -a vv1 -r ctgA -s 1 -e 10`) + p = new Shell(`${apollo} assembly sequence ${P} -a vv1 -r ctgA -s 1 -e 10`) const seq = p.stdout.trim().split('\n') assert.strictEqual(seq[1], 'cattgttgcg') p = new Shell( - `${apollo} assembly add-from-fasta -a vv1 -f https://x.fa.gz --fai https://x.fa.gz.fai --gzi https://x.fa.gz.gzi`, + `${apollo} assembly add-from-fasta ${P} -a vv1 -f https://x.fa.gz --fai https://x.fa.gz.fai --gzi https://x.fa.gz.gzi`, false, ) assert.ok(p.returncode != 0) @@ -470,7 +490,7 @@ void describe('Test CLI', () => { void globalThis.itName('Detect missing external index', () => { const p = new Shell( - `${apollo} assembly add-from-fasta -a vv1 -f http://localhost:3131/tiny.fasta`, + `${apollo} assembly add-from-fasta ${P} -a vv1 -f http://localhost:3131/tiny.fasta`, false, ) assert.ok(p.returncode != 0) @@ -482,7 +502,7 @@ void describe('Test CLI', () => { void globalThis.itName( 'Editable sequence not allowed with external source', () => { - const cmd = `${apollo} assembly add-from-fasta -a vv1 -f http://localhost:3131/tiny.fasta.gz` + const cmd = `${apollo} assembly add-from-fasta ${P} -a vv1 -f http://localhost:3131/tiny.fasta.gz` new Shell(cmd) const p = new Shell(`${cmd} -e`, false) @@ -493,13 +513,13 @@ void describe('Test CLI', () => { void globalThis.itName('Edit feature from json', () => { new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 -f`, ) - let p = new Shell(`${apollo} feature search -a vv1 -t BAC`) + let p = new Shell(`${apollo} feature search ${P} -a vv1 -t BAC`) let out = JSON.parse(p.stdout).at(0) assert.strictEqual(out.type, 'BAC') - p = new Shell(`${apollo} assembly get -a vv1`) + p = new Shell(`${apollo} assembly get ${P} -a vv1`) const asm_id = JSON.parse(p.stdout).at(0)._id const req = [ @@ -513,29 +533,29 @@ void describe('Test CLI', () => { }, ] const j = JSON.stringify(req) - new Shell(`echo '${j}' | ${apollo} feature edit -j -`) - p = new Shell(`${apollo} feature search -a vv1 -t G_quartet`) + new Shell(`echo '${j}' | ${apollo} feature edit ${P} -j -`) + p = new Shell(`${apollo} feature search ${P} -a vv1 -t G_quartet`) out = JSON.parse(p.stdout).at(0) assert.strictEqual(out.type, 'G_quartet') }) void globalThis.itName('Edit feature type', () => { new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 -f`, ) // Get id of assembly named vv1 - let p = new Shell(`${apollo} assembly get -a vv1`) + let p = new Shell(`${apollo} assembly get ${P} -a vv1`) const asm_id = JSON.parse(p.stdout).at(0)._id // Get refseqs in assembly vv1 p = new Shell( - `${apollo} refseq get | jq '.[] | select(.assembly == "${asm_id}" and .name == "ctgA") | ._id'`, + `${apollo} refseq get ${P} | jq '.[] | select(.assembly == "${asm_id}" and .name == "ctgA") | ._id'`, ) const refseq = p.stdout.trim() // Get feature in vv1 - p = new Shell(`${apollo} feature get -r ${refseq}`) + p = new Shell(`${apollo} feature get ${P} -r ${refseq}`) const features = JSON.parse(p.stdout) assert.ok(features.length > 2) @@ -545,36 +565,36 @@ void describe('Test CLI', () => { const contig_id = contig.at(0)._id // Edit type of "contig" feature - p = new Shell(`${apollo} feature edit-type -i ${contig_id} -t region`) + p = new Shell(`${apollo} feature edit-type ${P} -i ${contig_id} -t region`) p = new Shell( - `${apollo} feature get -r ${refseq} | jq '.[] | select(._id == "${contig_id}")'`, + `${apollo} feature get ${P} -r ${refseq} | jq '.[] | select(._id == "${contig_id}")'`, ) contig = JSON.parse(p.stdout) assert.deepStrictEqual(contig.type, 'region') // Return current type - p = new Shell(`${apollo} feature edit-type -i ${contig_id}`) + p = new Shell(`${apollo} feature edit-type ${P} -i ${contig_id}`) assert.deepStrictEqual(p.stdout.trim(), 'region') }) void globalThis.itName('Edit feature coords', () => { new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 -f`, ) // Get id of assembly named vv1 - let p = new Shell(`${apollo} assembly get -a vv1`) + let p = new Shell(`${apollo} assembly get ${P} -a vv1`) const asm_id = JSON.parse(p.stdout).at(0)._id // Get refseqs in assembly vv1 p = new Shell( - `${apollo} refseq get | jq '.[] | select(.assembly == "${asm_id}" and .name == "ctgA") | ._id'`, + `${apollo} refseq get ${P} | jq '.[] | select(.assembly == "${asm_id}" and .name == "ctgA") | ._id'`, ) const refseq = p.stdout.trim() // Get feature in vv1 - p = new Shell(`${apollo} feature get -r ${refseq}`) + p = new Shell(`${apollo} feature get ${P} -r ${refseq}`) const features = JSON.parse(p.stdout) assert.ok(features.length > 2) @@ -584,30 +604,35 @@ void describe('Test CLI', () => { const contig_id = contig.at(0)._id // Edit start and end coordinates - new Shell(`${apollo} feature edit-coords -i ${contig_id} -s 80 -e 160`) - new Shell(`${apollo} feature edit-coords -i ${contig_id} -s 20 -e 100`) + new Shell(`${apollo} feature edit-coords ${P} -i ${contig_id} -s 80 -e 160`) + new Shell(`${apollo} feature edit-coords ${P} -i ${contig_id} -s 20 -e 100`) p = new Shell( - `${apollo} feature get -r ${refseq} | jq '.[] | select(._id == "${contig_id}")'`, + `${apollo} feature get ${P} -r ${refseq} | jq '.[] | select(._id == "${contig_id}")'`, ) contig = JSON.parse(p.stdout) assert.strictEqual(contig.min, 20 - 1) assert.strictEqual(contig.max, 100) - p = new Shell(`${apollo} feature edit-coords -i ${contig_id} -s 1 -e 1`) p = new Shell( - `${apollo} feature get -r ${refseq} | jq '.[] | select(._id == "${contig_id}")'`, + `${apollo} feature edit-coords ${P} -i ${contig_id} -s 1 -e 1`, + ) + p = new Shell( + `${apollo} feature get ${P} -r ${refseq} | jq '.[] | select(._id == "${contig_id}")'`, ) contig = JSON.parse(p.stdout) assert.strictEqual(contig.min, 0) assert.strictEqual(contig.max, 1) - p = new Shell(`${apollo} feature edit-coords -i ${contig_id} -s 0`, false) + p = new Shell( + `${apollo} feature edit-coords ${P} -i ${contig_id} -s 0`, + false, + ) assert.strictEqual(p.returncode, 2) assert.ok(p.stderr.includes('Coordinates must be greater than 0')) p = new Shell( - `${apollo} feature edit-coords -i ${contig_id} -s 10 -e 9`, + `${apollo} feature edit-coords ${P} -i ${contig_id} -s 10 -e 9`, false, ) assert.strictEqual(p.returncode, 2) @@ -625,192 +650,199 @@ void describe('Test CLI', () => { ) assert.ok(eden_gene) const mrna_id = Object.keys(eden_gene.children).at(0) - p = new Shell(`${apollo} feature edit-coords -i ${mrna_id} -s 1`, false) + p = new Shell( + `${apollo} feature edit-coords ${P} -i ${mrna_id} -s 1`, + false, + ) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('exceeds the bounds of its parent')) }) void globalThis.itName('Edit attributes', () => { new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 -f`, ) // Get id of assembly named vv1 - let p = new Shell(`${apollo} assembly get -a vv1`) + let p = new Shell(`${apollo} assembly get ${P} -a vv1`) const asm_id = JSON.parse(p.stdout).at(0)._id p = new Shell( - `${apollo} refseq get | jq '.[] | select(.assembly == "${asm_id}" and .name == "ctgA") | ._id'`, + `${apollo} refseq get ${P} | jq '.[] | select(.assembly == "${asm_id}" and .name == "ctgA") | ._id'`, ) const refseq = p.stdout.trim() // Get feature in vv1 p = new Shell( - `${apollo} feature get -r ${refseq} | jq '.[] | select(.type == "contig") | ._id'`, + `${apollo} feature get ${P} -r ${refseq} | jq '.[] | select(.type == "contig") | ._id'`, ) const fid = p.stdout.trim() // Edit existing attribute value p = new Shell( - `${apollo} feature edit-attribute -i ${fid} -a source -v 'Eggs & Stuff'`, + `${apollo} feature edit-attribute ${P} -i ${fid} -a source -v 'Eggs & Stuff'`, ) - p = new Shell(`${apollo} feature edit-attribute -i ${fid} -a source`) + p = new Shell(`${apollo} feature edit-attribute ${P} -i ${fid} -a source`) let out = JSON.parse(p.stdout) assert.deepStrictEqual(out.at(0), `Eggs & Stuff`) // Add attribute - new Shell(`${apollo} feature edit-attribute -i ${fid} -a newAttr -v stuff`) - p = new Shell(`${apollo} feature edit-attribute -i ${fid} -a newAttr`) + new Shell( + `${apollo} feature edit-attribute ${P} -i ${fid} -a newAttr -v stuff`, + ) + p = new Shell(`${apollo} feature edit-attribute ${P} -i ${fid} -a newAttr`) assert.ok(p.stdout.includes('stuf')) // Non existing attr - p = new Shell(`${apollo} feature edit-attribute -i ${fid} -a NonExist`) + p = new Shell(`${apollo} feature edit-attribute ${P} -i ${fid} -a NonExist`) assert.deepStrictEqual(p.stdout.trim(), '') // List of values p = new Shell( - `${apollo} feature edit-attribute -i ${fid} -a newAttr -v A B C`, + `${apollo} feature edit-attribute ${P} -i ${fid} -a newAttr -v A B C`, ) - p = new Shell(`${apollo} feature edit-attribute -i ${fid} -a newAttr`) + p = new Shell(`${apollo} feature edit-attribute ${P} -i ${fid} -a newAttr`) out = JSON.parse(p.stdout) assert.deepStrictEqual(out, ['A', 'B', 'C']) // Delete attribute - new Shell(`${apollo} feature edit-attribute -i ${fid} -a newAttr -d`) - p = new Shell(`${apollo} feature edit-attribute -i ${fid} -a newAttr`) + new Shell(`${apollo} feature edit-attribute ${P} -i ${fid} -a newAttr -d`) + p = new Shell(`${apollo} feature edit-attribute ${P} -i ${fid} -a newAttr`) assert.deepStrictEqual(p.stdout.trim(), '') // Delete again is ok - new Shell(`${apollo} feature edit-attribute -i ${fid} -a newAttr -d`) + new Shell(`${apollo} feature edit-attribute ${P} -i ${fid} -a newAttr -d`) // Special fields p = new Shell( - `${apollo} feature edit-attribute -i ${fid} -a 'Gene Ontology' -v GO:0051728 GO:0019090`, + `${apollo} feature edit-attribute ${P} -i ${fid} -a 'Gene Ontology' -v GO:0051728 GO:0019090`, ) p = new Shell( - `${apollo} feature edit-attribute -i ${fid} -a 'Gene Ontology'`, + `${apollo} feature edit-attribute ${P} -i ${fid} -a 'Gene Ontology'`, ) out = JSON.parse(p.stdout) assert.deepStrictEqual(out, ['GO:0051728', 'GO:0019090']) // This should fail p = new Shell( - `${apollo} feature edit-attribute -i ${fid} -a 'Gene Ontology' -v FOOBAR`, + `${apollo} feature edit-attribute ${P} -i ${fid} -a 'Gene Ontology' -v FOOBAR`, ) }) void globalThis.itName('Search features', () => { new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 -f`, ) new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv2 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv2 -f`, ) - let p = new Shell(`${apollo} feature search -a vv1 vv2 -t EDEN`) + let p = new Shell(`${apollo} feature search ${P} -a vv1 vv2 -t EDEN`) let out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) assert.ok(p.stdout.includes('EDEN')) - p = new Shell(`${apollo} feature search -t EDEN`) + p = new Shell(`${apollo} feature search ${P} -t EDEN`) out = JSON.parse(p.stdout) assert.ok(out.length >= 2) - p = new Shell(`${apollo} feature search -a vv1 -t EDEN`) + p = new Shell(`${apollo} feature search ${P} -a vv1 -t EDEN`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) assert.ok(p.stdout.includes('EDEN')) - p = new Shell(`${apollo} feature search -a foobar -t EDEN`) + p = new Shell(`${apollo} feature search ${P} -a foobar -t EDEN`) assert.strictEqual('[]', p.stdout.trim()) assert.ok(p.stderr.includes('Warning')) - p = new Shell(`${apollo} feature search -a vv1 -t foobarspam`) + p = new Shell(`${apollo} feature search ${P} -a vv1 -t foobarspam`) assert.deepStrictEqual(p.stdout.trim(), '[]') // It searches attributes values, not attribute names - p = new Shell(`${apollo} feature search -a vv1 -t multivalue`) + p = new Shell(`${apollo} feature search ${P} -a vv1 -t multivalue`) assert.deepStrictEqual(p.stdout.trim(), '[]') // Search feature type - p = new Shell(`${apollo} feature search -a vv1 -t contig`) + p = new Shell(`${apollo} feature search ${P} -a vv1 -t contig`) assert.ok(p.stdout.includes('"type": "contig"')) // Search source (which in fact is an attribute) - p = new Shell(`${apollo} feature search -a vv1 -t someExample`) + p = new Shell(`${apollo} feature search ${P} -a vv1 -t someExample`) assert.ok(p.stdout.includes('SomeContig')) // Case insensitive - p = new Shell(`${apollo} feature search -a vv1 -t SOMEexample`) + p = new Shell(`${apollo} feature search ${P} -a vv1 -t SOMEexample`) assert.ok(p.stdout.includes('SomeContig')) // No partial word match - p = new Shell(`${apollo} feature search -a vv1 -t Fingerpri`) + p = new Shell(`${apollo} feature search ${P} -a vv1 -t Fingerpri`) assert.deepStrictEqual(p.stdout.trim(), '[]') // Match full word not necessarily full value - p = new Shell(`${apollo} feature search -a vv1 -t Fingerprinted`) + p = new Shell(`${apollo} feature search ${P} -a vv1 -t Fingerprinted`) assert.ok(p.stdout.includes('Fingerprinted')) // Does not search contig names (reference sequence name) - p = new Shell(`${apollo} feature search -a vv1 -t ctgB`) + p = new Shell(`${apollo} feature search ${P} -a vv1 -t ctgB`) assert.deepStrictEqual(p.stdout.trim(), '[]') // Does not match common words (?) ... - p = new Shell(`${apollo} feature search -a vv1 -t with`) + p = new Shell(`${apollo} feature search ${P} -a vv1 -t with`) assert.deepStrictEqual(p.stdout.trim(), '[]') // ...But "fake" is ok - p = new Shell(`${apollo} feature search -a vv1 -t fake`) + p = new Shell(`${apollo} feature search ${P} -a vv1 -t fake`) assert.ok(p.stdout.includes('FakeSNP1')) // ...or a single unusual letter - p = new Shell(`${apollo} feature search -a vv1 -t Q`) + p = new Shell(`${apollo} feature search ${P} -a vv1 -t Q`) assert.ok(p.stdout.includes('"Q"')) }) void globalThis.itName('Get feature by indexed ID', () => { new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 -f`, ) new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv2 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv2 -f`, ) // Search multiple assemblies - let p = new Shell(`${apollo} feature get-indexed-id MyGene -a vv1 vv2`) + let p = new Shell(`${apollo} feature get-indexed-id ${P} MyGene -a vv1 vv2`) let out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) assert.ok(p.stdout.includes('MyGene')) // Specifying no assembly defaults to searching all assemblies - p = new Shell(`${apollo} feature get-indexed-id MyGene`) + p = new Shell(`${apollo} feature get-indexed-id ${P} MyGene`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) assert.ok(p.stdout.includes('MyGene')) // Search single assembly - p = new Shell(`${apollo} feature get-indexed-id MyGene -a vv1`) + p = new Shell(`${apollo} feature get-indexed-id ${P} MyGene -a vv1`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) assert.ok(p.stdout.includes('MyGene')) // Warn on unknown assembly - p = new Shell(`${apollo} feature get-indexed-id EDEN -a foobar`) + p = new Shell(`${apollo} feature get-indexed-id ${P} EDEN -a foobar`) assert.strictEqual('[]', p.stdout.trim()) assert.ok(p.stderr.includes('Warning')) // Return empty array with no matches - p = new Shell(`${apollo} feature get-indexed-id foobarspam -a vv1`) + p = new Shell(`${apollo} feature get-indexed-id ${P} foobarspam -a vv1`) assert.deepStrictEqual(p.stdout.trim(), '[]') // Gets subfeature - p = new Shell(`${apollo} feature get-indexed-id myCDS.1 -a vv1`) + p = new Shell(`${apollo} feature get-indexed-id ${P} myCDS.1 -a vv1`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) assert.ok(out.at(0)?.type === 'CDS') // Gets top-level feature from subfeature id - p = new Shell(`${apollo} feature get-indexed-id myCDS.1 -a vv1 --topLevel`) + p = new Shell( + `${apollo} feature get-indexed-id ${P} myCDS.1 -a vv1 --topLevel`, + ) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) assert.ok(out.at(0)?.type === 'gene') @@ -818,60 +850,62 @@ void describe('Test CLI', () => { void globalThis.itName('Delete features', () => { new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 -f`, ) - let p = new Shell(`${apollo} feature search -a vv1 -t EDEN`) + let p = new Shell(`${apollo} feature search ${P} -a vv1 -t EDEN`) const fid = JSON.parse(p.stdout).at(0)._id - p = new Shell(`${apollo} feature delete -i ${fid} --dry-run`) + p = new Shell(`${apollo} feature delete ${P} -i ${fid} --dry-run`) assert.ok(p.stdout.includes(fid)) - new Shell(`${apollo} feature delete -i ${fid}`) - p = new Shell(`${apollo} feature search -a vv1 -t EDEN`) + new Shell(`${apollo} feature delete ${P} -i ${fid}`) + p = new Shell(`${apollo} feature search ${P} -a vv1 -t EDEN`) assert.deepStrictEqual(p.stdout.trim(), '[]') - p = new Shell(`${apollo} feature delete -i ${fid}`, false) + p = new Shell(`${apollo} feature delete ${P} -i ${fid}`, false) assert.strictEqual(p.returncode, 1) assert.ok( p.stderr.includes('The following featureId was not found in database'), ) - p = new Shell(`${apollo} feature delete --force -i ${fid}`) + p = new Shell(`${apollo} feature delete ${P} --force -i ${fid}`) assert.strictEqual(p.returncode, 0) }) void globalThis.itName('Add features', () => { let p = new Shell( - `${apollo} assembly add-from-fasta test_data/tiny.fasta.gz -a tiny -f`, + `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta.gz -a tiny -f`, ) let out = JSON.parse(p.stdout) const assemblyId = out._id - p = new Shell(`${apollo} feature get -a tiny`) + p = new Shell(`${apollo} feature get ${P} -a tiny`) assert.deepStrictEqual(p.stdout.trim(), '[]') // Can add a feature using flags - p = new Shell(`${apollo} feature add -a tiny -r ctgA -s 1 -e 10 -t remark`) + p = new Shell( + `${apollo} feature add ${P} -a tiny -r ctgA -s 1 -e 10 -t remark`, + ) out = JSON.parse(p.stdout) - p = new Shell(`${apollo} feature get -a tiny`) + p = new Shell(`${apollo} feature get ${P} -a tiny`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) const refSeqId = out[0].refSeq // Can add a feature using assembly and refSeq ids p = new Shell( - `${apollo} feature add -a ${assemblyId} -r ${refSeqId} -s 11 -e 20 -t remark`, + `${apollo} feature add ${P} -a ${assemblyId} -r ${refSeqId} -s 11 -e 20 -t remark`, ) - p = new Shell(`${apollo} feature get -a ${assemblyId}`) + p = new Shell(`${apollo} feature get ${P} -a ${assemblyId}`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) // Can add a feature using JSON arg p = new Shell( - `${apollo} feature add '{"assembly":"${assemblyId}","refSeq":"${refSeqId}","min":21,"max":30,"type":"remark"}'`, + `${apollo} feature add ${P} '{"assembly":"${assemblyId}","refSeq":"${refSeqId}","min":21,"max":30,"type":"remark"}'`, ) - p = new Shell(`${apollo} feature get -a ${assemblyId}`) + p = new Shell(`${apollo} feature get ${P} -a ${assemblyId}`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 3) // Can add a feature using JSON from stdin p = new Shell( - `${apollo} feature add < { } EOF`, ) - p = new Shell(`${apollo} feature get -a ${assemblyId}`) + p = new Shell(`${apollo} feature get ${P} -a ${assemblyId}`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 4) // Can add a feature using JSON from a file @@ -890,45 +924,45 @@ EOF`, `{"assembly":"${assemblyId}","refSeq":"${refSeqId}","min":41,"max":50,"type":"remark"}\n`, ) p = new Shell( - `${apollo} feature add --feature-json-file test_data/tmp.json`, + `${apollo} feature add ${P} --feature-json-file test_data/tmp.json`, ) fs.unlinkSync('test_data/tmp.json') - p = new Shell(`${apollo} feature get -a ${assemblyId}`) + p = new Shell(`${apollo} feature get ${P} -a ${assemblyId}`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 5) // Can add multiple features using JSON p = new Shell( - `${apollo} feature add '[{"assembly":"${assemblyId}","refSeq":"${refSeqId}","min":51,"max":60,"type":"remark"},{"assembly":"${assemblyId}","refSeq":"${refSeqId}","min":61,"max":70,"type":"remark"}]'`, + `${apollo} feature add ${P} '[{"assembly":"${assemblyId}","refSeq":"${refSeqId}","min":51,"max":60,"type":"remark"},{"assembly":"${assemblyId}","refSeq":"${refSeqId}","min":61,"max":70,"type":"remark"}]'`, ) - p = new Shell(`${apollo} feature get -a ${assemblyId}`) + p = new Shell(`${apollo} feature get ${P} -a ${assemblyId}`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 7) // Can add a feature with children from JSON p = new Shell( - `${apollo} feature add '{"assembly":"${assemblyId}","refSeq":"${refSeqId}","min":71,"max":80,"type":"match","children":[{"min":71,"max":75,"type":"match_part"}]}'`, + `${apollo} feature add ${P} '{"assembly":"${assemblyId}","refSeq":"${refSeqId}","min":71,"max":80,"type":"match","children":[{"min":71,"max":75,"type":"match_part"}]}'`, ) p = new Shell( - `${apollo} feature get -a ${assemblyId} -r ${refSeqId} -s 71 -e 80`, + `${apollo} feature get ${P} -a ${assemblyId} -r ${refSeqId} -s 71 -e 80`, ) out = JSON.parse(p.stdout) let feature = out.at(0) assert.strictEqual(Object.keys(feature?.children).length, 1) // Can add a feature with attributes from JSON p = new Shell( - `${apollo} feature add '{"assembly":"${assemblyId}","refSeq":"${refSeqId}","min":81,"max":90,"type":"remark","attributes":{"key1":["val1"]}}'`, + `${apollo} feature add ${P} '{"assembly":"${assemblyId}","refSeq":"${refSeqId}","min":81,"max":90,"type":"remark","attributes":{"key1":["val1"]}}'`, ) p = new Shell( - `${apollo} feature get -a ${assemblyId} -r ${refSeqId} -s 81 -e 90`, + `${apollo} feature get ${P} -a ${assemblyId} -r ${refSeqId} -s 81 -e 90`, ) out = JSON.parse(p.stdout) feature = out.at(0) assert.strictEqual(feature?.attributes?.key1?.[0], 'val1') // Can add a feature with children from JSON p = new Shell( - `${apollo} feature add '{"assembly":"${assemblyId}","refSeq":"${refSeqId}","min":91,"max":100,"type":"match","children":[{"min":91,"max":95,"type":"match_part","attributes":{"key2":["val2"]}}]}'`, + `${apollo} feature add ${P} '{"assembly":"${assemblyId}","refSeq":"${refSeqId}","min":91,"max":100,"type":"match","children":[{"min":91,"max":95,"type":"match_part","attributes":{"key2":["val2"]}}]}'`, ) p = new Shell( - `${apollo} feature get -a ${assemblyId} -r ${refSeqId} -s 91 -e 100`, + `${apollo} feature get ${P} -a ${assemblyId} -r ${refSeqId} -s 91 -e 100`, ) out = JSON.parse(p.stdout) feature = out.at(0) @@ -939,21 +973,21 @@ EOF`, void globalThis.itName('Add child features', () => { new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a vv1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a vv1 -f`, ) - let p = new Shell(`${apollo} feature search -a vv1 -t contig`) + let p = new Shell(`${apollo} feature search ${P} -a vv1 -t contig`) const fid = JSON.parse(p.stdout).at(0)._id new Shell( - `${apollo} feature add-child -i ${fid} -s 10 -e 20 -t contig_read`, + `${apollo} feature add-child ${P} -i ${fid} -s 10 -e 20 -t contig_read`, ) - p = new Shell(`${apollo} feature search -a vv1 -t contig_read`) + p = new Shell(`${apollo} feature search ${P} -a vv1 -t contig_read`) assert.ok(p.stdout.includes('contig_read')) assert.ok(p.stdout.includes('"min": 9')) assert.ok(p.stdout.includes('"max": 20')) p = new Shell( - `${apollo} feature add-child -i ${fid} -s 10 -e 2000 -t contig_read`, + `${apollo} feature add-child ${P} -i ${fid} -s 10 -e 2000 -t contig_read`, false, ) assert.ok(p.returncode != 0) @@ -961,7 +995,7 @@ EOF`, // Should this fail? p = new Shell( - `${apollo} feature add-child -i ${fid} -s 10 -e 20 -t FOOBAR`, + `${apollo} feature add-child ${P} -i ${fid} -s 10 -e 20 -t FOOBAR`, false, ) assert.strictEqual(p.returncode, 0) @@ -969,114 +1003,116 @@ EOF`, void globalThis.itName('Import features', () => { new Shell( - `${apollo} assembly add-from-fasta test_data/tiny.fasta -a vv1 -e -f`, + `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a vv1 -e -f`, ) - new Shell(`${apollo} feature import test_data/tiny.fasta.gff3 -a vv1`) - let p = new Shell(`${apollo} feature search -a vv1 -t contig`) + new Shell(`${apollo} feature import ${P} test_data/tiny.fasta.gff3 -a vv1`) + let p = new Shell(`${apollo} feature search ${P} -a vv1 -t contig`) let out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) // Import again: Add to existing feature - p = new Shell(`${apollo} feature import test_data/tiny.fasta.gff3 -a vv1`) - p = new Shell(`${apollo} feature search -a vv1 -t contig`) + p = new Shell( + `${apollo} feature import ${P} test_data/tiny.fasta.gff3 -a vv1`, + ) + p = new Shell(`${apollo} feature search ${P} -a vv1 -t contig`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 4) - // Import again: delete existing + // Import again: delete ${P} existing p = new Shell( - `${apollo} feature import -d test_data/tiny.fasta.gff3 -a vv1`, + `${apollo} feature import ${P} -d test_data/tiny.fasta.gff3 -a vv1`, ) - p = new Shell(`${apollo} feature search -a vv1 -t contig`) + p = new Shell(`${apollo} feature search ${P} -a vv1 -t contig`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) - p = new Shell(`${apollo} assembly delete -a vv2`) + p = new Shell(`${apollo} assembly delete ${P} -a vv2`) p = new Shell( - `${apollo} feature import test_data/tiny.fasta.gff3 -a vv2`, + `${apollo} feature import ${P} test_data/tiny.fasta.gff3 -a vv2`, false, ) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('Assembly "vv2" does not exist')) - p = new Shell(`${apollo} feature import foo.gff3 -a vv1`, false) + p = new Shell(`${apollo} feature import ${P} foo.gff3 -a vv1`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('File "foo.gff3" does not exist')) }) void globalThis.itName('Copy feature', () => { new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a source -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a source -f`, ) new Shell( - `${apollo} assembly add-from-fasta test_data/tiny.fasta -a dest -e -f`, + `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a dest -e -f`, ) new Shell( - `${apollo} assembly add-from-fasta test_data/tiny.fasta -a dest2 -e -f`, + `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a dest2 -e -f`, ) - let p = new Shell(`${apollo} feature search -a source -t contig`) + let p = new Shell(`${apollo} feature search ${P} -a source -t contig`) const fid = JSON.parse(p.stdout).at(0)._id - new Shell(`${apollo} feature copy -i ${fid} -r ctgA -a dest -s 1`) - p = new Shell(`${apollo} feature search -a dest -t contig`) + new Shell(`${apollo} feature copy ${P} -i ${fid} -r ctgA -a dest -s 1`) + p = new Shell(`${apollo} feature search ${P} -a dest -t contig`) let out = JSON.parse(p.stdout).at(0) assert.strictEqual(out.min, 0) assert.strictEqual(out.max, 50) // RefSeq id does not need assembly - p = new Shell(`${apollo} refseq get -a dest2`) + p = new Shell(`${apollo} refseq get ${P} -a dest2`) const destRefSeq = JSON.parse(p.stdout).find( (x: any) => x.name === 'ctgA', )._id - p = new Shell(`${apollo} feature copy -i ${fid} -r ${destRefSeq} -s 2`) - p = new Shell(`${apollo} feature search -a dest2 -t contig`) + p = new Shell(`${apollo} feature copy ${P} -i ${fid} -r ${destRefSeq} -s 2`) + p = new Shell(`${apollo} feature search ${P} -a dest2 -t contig`) out = JSON.parse(p.stdout).at(0) assert.strictEqual(out.min, 1) assert.strictEqual(out.max, 51) // Copy to same assembly - new Shell(`${apollo} feature copy -i ${fid} -r ctgA -a source -s 10`) - p = new Shell(`${apollo} feature search -a source -t contig`) + new Shell(`${apollo} feature copy ${P} -i ${fid} -r ctgA -a source -s 10`) + p = new Shell(`${apollo} feature search ${P} -a source -t contig`) out = JSON.parse(p.stdout) // Copy non-existant feature or refseq p = new Shell( - `${apollo} feature copy -i FOOBAR -r ctgA -a dest -s 1`, + `${apollo} feature copy ${P} -i FOOBAR -r ctgA -a dest -s 1`, false, ) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('ERROR')) p = new Shell( - `${apollo} feature copy -i ${fid} -r FOOBAR -a dest -s 1`, + `${apollo} feature copy ${P} -i ${fid} -r FOOBAR -a dest -s 1`, false, ) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('No reference')) // Ambiguous refseq - p = new Shell(`${apollo} feature copy -i ${fid} -r ctgA -s 1`, false) + p = new Shell(`${apollo} feature copy ${P} -i ${fid} -r ctgA -s 1`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('more than one')) }) void globalThis.itName('Get changes', () => { new Shell( - `${apollo} assembly add-from-fasta test_data/tiny.fasta -a myAssembly -e -f`, + `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a myAssembly -e -f`, ) new Shell( - `${apollo} assembly add-from-fasta test_data/tiny.fasta -a yourAssembly -e -f`, + `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a yourAssembly -e -f`, ) new Shell( - `${apollo} assembly add-from-fasta test_data/tiny.fasta -a ourAssembly -e -f`, + `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a ourAssembly -e -f`, ) - let p = new Shell(`${apollo} change get`) + let p = new Shell(`${apollo} change get ${P}`) JSON.parse(p.stdout) assert.ok(p.stdout.includes('myAssembly')) assert.ok(p.stdout.includes('yourAssembly')) - p = new Shell(`${apollo} change get -a myAssembly ourAssembly`) + p = new Shell(`${apollo} change get ${P} -a myAssembly ourAssembly`) assert.ok(p.stdout.includes('myAssembly')) assert.ok(p.stdout.includes('ourAssembly')) assert.ok(p.stdout.includes('yourAssembly') == false) @@ -1085,30 +1121,30 @@ EOF`, // returned because the assemblies collection doesn't contain that name // anymore. Ideally you should still be able to get changes by name? new Shell( - `${apollo} assembly delete -a myAssembly yourAssembly ourAssembly`, + `${apollo} assembly delete ${P} -a myAssembly yourAssembly ourAssembly`, ) - p = new Shell(`${apollo} change get -a myAssembly`) + p = new Shell(`${apollo} change get ${P} -a myAssembly`) const out = JSON.parse(p.stdout) assert.strictEqual(out.length, 0) }) void globalThis.itName('Get sequence', () => { new Shell( - `${apollo} assembly add-from-fasta test_data/tiny.fasta -a v1 -e -f`, + `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a v1 -e -f`, ) new Shell( - `${apollo} assembly add-from-fasta test_data/tiny.fasta -a v2 -e -f`, + `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta -a v2 -e -f`, ) - let p = new Shell(`${apollo} assembly sequence -a nonExistant`, false) + let p = new Shell(`${apollo} assembly sequence ${P} -a nonExistant`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('returned 0 assemblies')) - p = new Shell(`${apollo} assembly sequence -a v1 -s 0`, false) + p = new Shell(`${apollo} assembly sequence ${P} -a v1 -s 0`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('must be greater than 0')) - p = new Shell(`${apollo} assembly sequence -a v1`) + p = new Shell(`${apollo} assembly sequence ${P} -a v1`) let seq = p.stdout.trim().split('\n') assert.strictEqual(seq.length, 25) assert.deepStrictEqual(seq.at(0), '>ctgA:1..420') @@ -1120,40 +1156,40 @@ EOF`, assert.deepStrictEqual(seq.at(7), '>ctgB:1..800') assert.deepStrictEqual(seq.at(-1), 'ttggtcgctccgttgtaccc') - p = new Shell(`${apollo} assembly sequence -a v1 -r ctgB -s 1 -e 1`) + p = new Shell(`${apollo} assembly sequence ${P} -a v1 -r ctgB -s 1 -e 1`) seq = p.stdout.split('\n') assert.deepStrictEqual(seq.at(0), '>ctgB:1..1') assert.deepStrictEqual(seq.at(1), 'A') - p = new Shell(`${apollo} assembly sequence -a v1 -r ctgB -s 2 -e 4`) + p = new Shell(`${apollo} assembly sequence ${P} -a v1 -r ctgB -s 2 -e 4`) seq = p.stdout.split('\n') assert.deepStrictEqual(seq.at(0), '>ctgB:2..4') assert.deepStrictEqual(seq.at(1), 'CAT') - p = new Shell(`${apollo} assembly sequence -r ctgB`, false) + p = new Shell(`${apollo} assembly sequence ${P} -r ctgB`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('found in more than one')) }) void globalThis.itName('Get feature by id', () => { new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a v1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a v1 -f`, ) - let p = new Shell(`${apollo} feature get -a v1`) + let p = new Shell(`${apollo} feature get ${P} -a v1`) const ff = JSON.parse(p.stdout) const x1 = ff.at(0)._id const x2 = ff.at(1)._id - p = new Shell(`${apollo} feature get-id -i ${x1} ${x1} ${x2}`) + p = new Shell(`${apollo} feature get-id ${P} -i ${x1} ${x1} ${x2}`) let out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) assert.deepStrictEqual(out.at(0)._id, x1) assert.deepStrictEqual(out.at(1)._id, x2) - p = new Shell(`${apollo} feature get-id -i FOOBAR`) + p = new Shell(`${apollo} feature get-id ${P} -i FOOBAR`) assert.deepStrictEqual(p.stdout.trim(), '[]') - p = new Shell(`echo -e '${x1} \n ${x2}' | ${apollo} feature get-id`) + p = new Shell(`echo -e '${x1} \n ${x2}' | ${apollo} feature get-id ${P}`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) }) @@ -1162,52 +1198,52 @@ EOF`, // TODO: Improve tests once more checks exist (currently there is only // CDSCheck) new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a v1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a v1 -f`, ) // Test view available check type - let p = new Shell(`${apollo} assembly check`) + let p = new Shell(`${apollo} assembly check ${P}`) let out = JSON.parse(p.stdout) assert.ok(p.stdout.includes('CDSCheck')) assert.ok(p.stdout.includes('TranscriptCheck')) const cdsCheckId = out.find((x: any) => x.name === 'CDSCheck')._id // Test view checks set for assembly - p = new Shell(`${apollo} assembly check -a v1`) + p = new Shell(`${apollo} assembly check ${P} -a v1`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) // Test non-existant assembly - p = new Shell(`${apollo} assembly check -a non-existant`, false) + p = new Shell(`${apollo} assembly check ${P} -a non-existant`, false) assert.strictEqual(p.returncode, 1) assert.ok(p.stderr.includes('non-existant')) // Test non-existant check - p = new Shell(`${apollo} assembly check -a v1 -c not-a-check`, false) + p = new Shell(`${apollo} assembly check ${P} -a v1 -c not-a-check`, false) assert.strictEqual(p.returncode, 1) assert.ok(p.stderr.includes('not-a-check')) // Test add checks. Test check is added as opposed to replacing current // checks with input list - new Shell(`${apollo} assembly check -a v1 -c CDSCheck CDSCheck`) - p = new Shell(`${apollo} assembly check -a v1`) + new Shell(`${apollo} assembly check ${P} -a v1 -c CDSCheck CDSCheck`) + p = new Shell(`${apollo} assembly check ${P} -a v1`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) assert.deepStrictEqual(out.at(0).name, 'CDSCheck') // Works also with check id new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a v2 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a v2 -f`, ) - new Shell(`${apollo} assembly check -a v2 -c ${cdsCheckId}`) - p = new Shell(`${apollo} assembly check -a v2`) + new Shell(`${apollo} assembly check ${P} -a v2 -c ${cdsCheckId}`) + p = new Shell(`${apollo} assembly check ${P} -a v2`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) assert.deepStrictEqual(out.at(0).name, 'CDSCheck') // Delete check - new Shell(`${apollo} assembly check -a v1 -d -c CDSCheck`) - p = new Shell(`${apollo} assembly check -a v1`) + new Shell(`${apollo} assembly check ${P} -a v1 -d -c CDSCheck`) + p = new Shell(`${apollo} assembly check ${P} -a v1`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) assert.ok(!p.stdout.includes('CDSCheck')) @@ -1216,10 +1252,10 @@ EOF`, void globalThis.itName('Feature checks', () => { new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a v1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a v1 -f`, ) - new Shell(`${apollo} assembly check -a v1 -c CDSCheck`) - let p = new Shell(`${apollo} feature check -a v1`) + new Shell(`${apollo} assembly check ${P} -a v1 -c CDSCheck`) + let p = new Shell(`${apollo} feature check ${P} -a v1`) const out = JSON.parse(p.stdout) assert.ok(out.length > 1) assert.ok(p.stdout.includes('InternalStopCodon')) @@ -1230,17 +1266,19 @@ EOF`, // Retrieve by feature id const xid = [...ids].join(' ') - p = new Shell(`${apollo} feature check -i ${xid}`) + p = new Shell(`${apollo} feature check ${P} -i ${xid}`) assert.ok(p.stdout.includes('InternalStopCodon')) }) void globalThis.itName('Feature checks indexed', () => { new Shell( - `${apollo} assembly add-from-fasta -a v1 test_data/tiny.fasta.gz -f`, + `${apollo} assembly add-from-fasta ${P} -a v1 test_data/tiny.fasta.gz -f`, + ) + new Shell(`${apollo} assembly check ${P} -a v1 -c CDSCheck`) + new Shell( + `${apollo} feature import ${P} -a v1 test_data/tiny.fasta.gff3 -d`, ) - new Shell(`${apollo} assembly check -a v1 -c CDSCheck`) - new Shell(`${apollo} feature import -a v1 test_data/tiny.fasta.gff3 -d`) - let p = new Shell(`${apollo} feature check -a v1`) + let p = new Shell(`${apollo} feature check ${P} -a v1`) const out = JSON.parse(p.stdout) assert.ok(out.length > 1) assert.ok(p.stdout.includes('InternalStopCodon')) @@ -1251,7 +1289,7 @@ EOF`, // Retrieve by feature id const xid = [...ids].join(' ') - p = new Shell(`${apollo} feature check -i ${xid}`) + p = new Shell(`${apollo} feature check ${P} -i ${xid}`) assert.ok(p.stdout.includes('InternalStopCodon')) }) @@ -1259,25 +1297,27 @@ EOF`, 'Delete check results when unregistering a check', () => { new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a v1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a v1 -f`, ) - let p = new Shell(`${apollo} feature check -a v1`) + let p = new Shell(`${apollo} feature check ${P} -a v1`) let checkResults = JSON.parse(p.stdout) as CheckResultSnapshot[] assert.ok(checkResults.length > 1) // Delete all checks and consequently delete all check results - p = new Shell(`${apollo} assembly check -a v1`) + p = new Shell(`${apollo} assembly check ${P} -a v1`) const checkNames = (JSON.parse(p.stdout) as CheckResultSnapshot[]).map( (x) => x.name, ) - new Shell(`${apollo} assembly check -a v1 -d -c ${checkNames.join(' ')}`) - p = new Shell(`${apollo} feature check -a v1`) + new Shell( + `${apollo} assembly check ${P} -a v1 -d -c ${checkNames.join(' ')}`, + ) + p = new Shell(`${apollo} feature check ${P} -a v1`) checkResults = JSON.parse(p.stdout) assert.deepEqual(checkResults.length, 0) // Put one check back - new Shell(`${apollo} assembly check -a v1 -c CDSCheck`) - p = new Shell(`${apollo} feature check -a v1`) + new Shell(`${apollo} assembly check ${P} -a v1 -c CDSCheck`) + p = new Shell(`${apollo} feature check ${P} -a v1`) checkResults = JSON.parse(p.stdout) assert.ok(checkResults.length > 0) assert.deepEqual( @@ -1288,20 +1328,20 @@ EOF`, ) void globalThis.itName('User', () => { - let p = new Shell(`${apollo} user get`) + let p = new Shell(`${apollo} user get ${P}`) let out = JSON.parse(p.stdout) assert.ok(out.length > 0) - p = new Shell(`${apollo} user get -r admin`) + p = new Shell(`${apollo} user get ${P} -r admin`) const out2 = JSON.parse(p.stdout) assert.ok(out.length > 0) assert.ok(out.length > out2.length) - p = new Shell(`${apollo} user get -r admin -u root`) + p = new Shell(`${apollo} user get ${P} -r admin -u root`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) - p = new Shell(`${apollo} user get -r readOnly -u root`) + p = new Shell(`${apollo} user get ${P} -r readOnly -u root`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 0) }) @@ -1361,22 +1401,22 @@ EOF`, void globalThis.itName('Refname alias configuration', () => { new Shell( - `${apollo} assembly add-from-gff test_data/tiny.fasta.gff3 -a asm1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/tiny.fasta.gff3 -a asm1 -f`, ) - let p = new Shell(`${apollo} assembly get -a asm1`) + let p = new Shell(`${apollo} assembly get ${P} -a asm1`) assert.ok(p.stdout.includes('asm1')) assert.ok(p.stdout.includes('asm2') == false) const asm_id = JSON.parse(p.stdout)[0]._id p = new Shell( - `${apollo} refseq add-alias test_data/alias.txt -a asm2`, + `${apollo} refseq add-alias ${P} test_data/alias.txt -a asm2`, false, ) assert.ok(p.stderr.includes('Assembly asm2 not found')) p = new Shell( - `${apollo} refseq add-alias test_data/alias.txt -a asm1`, + `${apollo} refseq add-alias ${P} test_data/alias.txt -a asm1`, false, ) assert.ok( @@ -1385,7 +1425,7 @@ EOF`, ), ) - p = new Shell(`${apollo} refseq get`) + p = new Shell(`${apollo} refseq get ${P}`) const refseq = JSON.parse(p.stdout.trim()) const vv1ref = refseq.filter((x: any) => x.assembly === asm_id) const refname_aliases: Record = {} @@ -1409,30 +1449,30 @@ EOF`, // Works locally but fails on github void globalThis.itName('Login', () => { // This should wait for user's input - const p = new Shell(`${apollo} login`, false, 5000) + const p = new Shell(`${apollo} login ${P}`, false, 5000) assert.ok(p.returncode != 0) // This should be ok - new Shell(`${apollo} login --force`, true, 5000) + new Shell(`${apollo} login ${P} --force`, true, 5000) }) void globalThis.itName('File upload', () => { - let p = new Shell(`${apollo} file upload test_data/tiny.fasta`) + let p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta`) let out = JSON.parse(p.stdout) assert.deepStrictEqual(out.type, 'text/x-fasta') assert.ok(out._id) - p = new Shell(`${apollo} file upload test_data/tiny.fasta`) + p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta`) out = JSON.parse(p.stdout) assert.deepStrictEqual(out.type, 'text/x-fasta') - p = new Shell(`${apollo} file upload test_data/tiny.fasta.gff3`) + p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta.gff3`) out = JSON.parse(p.stdout) assert.deepStrictEqual(out.type, 'text/x-gff3') - p = new Shell(`${apollo} file upload test_data/guest.yaml`, false) + p = new Shell(`${apollo} file upload ${P} test_data/guest.yaml`, false) assert.ok(p.returncode != 0) - p = new Shell(`${apollo} file upload test_data/tiny.fasta.gz`, false) + p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta.gz`, false) assert.ok(p.stderr.includes('it may be gzip or bgzip compressed')) assert.ok(p.returncode != 0) }) @@ -1443,43 +1483,43 @@ EOF`, const md5 = crypto.createHash('md5').update(gz).digest('hex') const p = new Shell( - `${apollo} file upload test_data/tiny.fasta.gz -t text/x-fasta`, + `${apollo} file upload ${P} test_data/tiny.fasta.gz -t text/x-fasta`, ) const out = JSON.parse(p.stdout) assert.strictEqual(out.checksum, md5) - new Shell(`${apollo} assembly add-from-fasta -e -f ${out._id}`) + new Shell(`${apollo} assembly add-from-fasta ${P} -e -f ${out._id}`) }) void globalThis.itName('Add assembly gzip', () => { // Autodetect format new Shell( - `${apollo} assembly add-from-fasta test_data/tiny.fasta.gz -e -f -a vv1`, + `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta.gz -e -f -a vv1`, ) - let p = new Shell(`${apollo} assembly sequence -a vv1`) + let p = new Shell(`${apollo} assembly sequence ${P} -a vv1`) assert.ok(p.stdout.startsWith('>')) assert.ok(p.stdout.includes('cattgttgcggagttgaaca')) // Skip autodetect fs.copyFileSync('test_data/tiny.fasta', 'test_data/tmp.gz') new Shell( - `${apollo} assembly add-from-fasta test_data/tmp.gz -e -f -a vv1 --decompressed`, + `${apollo} assembly add-from-fasta ${P} test_data/tmp.gz -e -f -a vv1 --decompressed`, ) - p = new Shell(`${apollo} assembly sequence -a vv1`) + p = new Shell(`${apollo} assembly sequence ${P} -a vv1`) assert.ok(p.stdout.startsWith('>')) assert.ok(p.stdout.includes('cattgttgcggagttgaaca')) fs.unlinkSync('test_data/tmp.gz') fs.copyFileSync('test_data/tiny.fasta.gz', 'test_data/fasta.tmp') new Shell( - `${apollo} assembly add-from-fasta test_data/fasta.tmp -e -f -a vv1 --gzip`, + `${apollo} assembly add-from-fasta ${P} test_data/fasta.tmp -e -f -a vv1 --gzip`, ) - p = new Shell(`${apollo} assembly sequence -a vv1`) + p = new Shell(`${apollo} assembly sequence ${P} -a vv1`) assert.ok(p.stdout.startsWith('>')) assert.ok(p.stdout.includes('cattgttgcggagttgaaca')) // Autodetect false positive p = new Shell( - `${apollo} assembly add-from-fasta test_data/fasta.tmp -e -f -a vv1`, + `${apollo} assembly add-from-fasta ${P} test_data/fasta.tmp -e -f -a vv1`, false, ) assert.ok(p.returncode != 0) @@ -1488,13 +1528,15 @@ EOF`, void globalThis.itName('Add editable assembly', () => { // It would be good to check that really there was no sequence loading - new Shell(`${apollo} assembly add-from-fasta -f test_data/tiny.fasta.gz`) - let p = new Shell(`${apollo} assembly sequence -a tiny.fasta.gz`) + new Shell( + `${apollo} assembly add-from-fasta ${P} -f test_data/tiny.fasta.gz`, + ) + let p = new Shell(`${apollo} assembly sequence ${P} -a tiny.fasta.gz`) assert.ok(p.stdout.startsWith('>')) assert.ok(p.stdout.includes('cattgttgcggagttgaaca')) p = new Shell( - `${apollo} assembly add-from-fasta -f test_data/tiny.fasta`, + `${apollo} assembly add-from-fasta ${P} -f test_data/tiny.fasta`, false, ) assert.ok(p.returncode != 0) @@ -1502,9 +1544,9 @@ EOF`, // Setting --gzi & --fai new Shell( - `${apollo} assembly add-from-fasta -f test_data/tiny2.fasta.gz --gzi test_data/tiny.fasta.gz.gzi --fai test_data/tiny.fasta.gz.fai`, + `${apollo} assembly add-from-fasta ${P} -f test_data/tiny2.fasta.gz --gzi test_data/tiny.fasta.gz.gzi --fai test_data/tiny.fasta.gz.fai`, ) - p = new Shell(`${apollo} assembly sequence -a tiny2.fasta.gz`) + p = new Shell(`${apollo} assembly sequence ${P} -a tiny2.fasta.gz`) assert.ok(p.stdout.startsWith('>')) assert.ok(p.stdout.includes('cattgttgcggagttgaaca')) }) @@ -1512,60 +1554,60 @@ EOF`, void globalThis.itName('Add assembly from file ids not editable', () => { // Upload and get Ids for: bgzip fasta, fai and gzi let p = new Shell( - `${apollo} file upload test_data/tiny.fasta.gz -t application/x-bgzip-fasta`, + `${apollo} file upload ${P} test_data/tiny.fasta.gz -t application/x-bgzip-fasta`, ) const fastaId = JSON.parse(p.stdout)._id - p = new Shell(`${apollo} file upload test_data/tiny.fasta.gz.fai`) + p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta.gz.fai`) const faiId = JSON.parse(p.stdout)._id - p = new Shell(`${apollo} file upload test_data/tiny.fasta.gz.gzi`) + p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta.gz.gzi`) const gziId = JSON.parse(p.stdout)._id new Shell( - `${apollo} assembly add-from-fasta -f ${fastaId} --fai test_data/tiny.fasta.gz.fai --gzi test_data/tiny.fasta.gz.gzi`, + `${apollo} assembly add-from-fasta ${P} -f ${fastaId} --fai test_data/tiny.fasta.gz.fai --gzi test_data/tiny.fasta.gz.gzi`, ) - p = new Shell(`${apollo} assembly sequence -a ${fastaId}`) + p = new Shell(`${apollo} assembly sequence ${P} -a ${fastaId}`) assert.ok(p.stdout.startsWith('>')) assert.ok(p.stdout.includes('cattgttgcggagttgaaca')) new Shell( - `${apollo} assembly add-from-fasta -f ${fastaId} --fai ${faiId} --gzi ${gziId}`, + `${apollo} assembly add-from-fasta ${P} -f ${fastaId} --fai ${faiId} --gzi ${gziId}`, ) - p = new Shell(`${apollo} assembly sequence -a ${fastaId}`) + p = new Shell(`${apollo} assembly sequence ${P} -a ${fastaId}`) assert.ok(p.stdout.startsWith('>')) }) void globalThis.itName('Add assembly from file id', () => { - let p = new Shell(`${apollo} file upload test_data/tiny.fasta`) + let p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta`) const fid = JSON.parse(p.stdout)._id - p = new Shell(`${apollo} assembly add-from-fasta ${fid} -a up -e -f`) + p = new Shell(`${apollo} assembly add-from-fasta ${P} ${fid} -a up -e -f`) const out = JSON.parse(p.stdout) assert.deepStrictEqual(out.name, 'up') assert.deepStrictEqual(out.fileIds.fa, fid) }) void globalThis.itName('Get files', () => { - new Shell(`${apollo} file upload test_data/tiny.fasta`) - let p = new Shell(`${apollo} file upload test_data/tiny.fasta`) + new Shell(`${apollo} file upload ${P} test_data/tiny.fasta`) + let p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta`) const fid = JSON.parse(p.stdout)._id - p = new Shell(`${apollo} file get`) + p = new Shell(`${apollo} file get ${P}`) let out = JSON.parse(p.stdout) assert.ok(out.length >= 2) assert.ok(out.filter((x: any) => x._id === fid)) - p = new Shell(`${apollo} file get -i ${fid} ${fid}`) + p = new Shell(`${apollo} file get ${P} -i ${fid} ${fid}`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) - p = new Shell(`${apollo} file get -i nonexists`) + p = new Shell(`${apollo} file get ${P} -i nonexists`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 0) }) void globalThis.itName('Download file', () => { - let p = new Shell(`${apollo} file upload test_data/tiny.fasta`) + let p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta`) const up = JSON.parse(p.stdout) if (fs.existsSync(up.basename)) { throw new Error( @@ -1573,68 +1615,68 @@ EOF`, ) } - new Shell(`${apollo} file download -i ${up._id}`) + new Shell(`${apollo} file download ${P} -i ${up._id}`) let down = fs.readFileSync(up.basename).toString() assert.ok(down.startsWith('>')) assert.ok(down.trim().endsWith('accc')) fs.unlinkSync(up.basename) - new Shell(`${apollo} file download -i ${up._id} -o tmp.fa`) + new Shell(`${apollo} file download ${P} -i ${up._id} -o tmp.fa`) down = fs.readFileSync('tmp.fa').toString() assert.ok(down.startsWith('>')) assert.ok(down.trim().endsWith('accc')) fs.unlinkSync('tmp.fa') - p = new Shell(`${apollo} file download -i ${up._id} -o -`) + p = new Shell(`${apollo} file download ${P} -i ${up._id} -o -`) assert.ok(p.stdout.startsWith('>')) assert.ok(p.stdout.trim().endsWith('accc')) }) void globalThis.itName('Delete file', () => { - let p = new Shell(`${apollo} file upload test_data/tiny.fasta`) + let p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta`) const up1 = JSON.parse(p.stdout) - p = new Shell(`${apollo} file upload test_data/tiny.fasta`) + p = new Shell(`${apollo} file upload ${P} test_data/tiny.fasta`) const up2 = JSON.parse(p.stdout) - p = new Shell(`${apollo} file delete -i ${up1._id} ${up2._id}`) + p = new Shell(`${apollo} file delete ${P} -i ${up1._id} ${up2._id}`) let out = JSON.parse(p.stdout) assert.strictEqual(out.length, 2) - p = new Shell(`${apollo} file get -i ${up1._id} ${up2._id}`) + p = new Shell(`${apollo} file get ${P} -i ${up1._id} ${up2._id}`) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 0) }) void globalThis.itName('Export gff3 from editable assembly', () => { new Shell( - `${apollo} assembly add-from-fasta test_data/tiny.fasta.gz -a vv1 -f --editable`, + `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta.gz -a vv1 -f --editable`, ) - new Shell(`${apollo} feature import test_data/tiny.fasta.gff3 -a vv1`) - let p = new Shell(`${apollo} export gff3 vv1 --include-fasta`) + new Shell(`${apollo} feature import ${P} test_data/tiny.fasta.gff3 -a vv1`) + let p = new Shell(`${apollo} export gff3 ${P} vv1 --include-fasta`) let gff = p.stdout assert.ok(gff.startsWith('##gff-version 3')) assert.ok(gff.includes('multivalue=val1,val2,val3')) assert.ok(gff.includes('##FASTA\n')) assert.deepStrictEqual(gff.slice(-6), 'taccc\n') - p = new Shell(`${apollo} export gff3 vv1`) + p = new Shell(`${apollo} export gff3 ${P} vv1`) gff = p.stdout assert.ok(gff.startsWith('##gff-version 3')) assert.ok(gff.includes('multivalue=val1,val2,val3')) assert.ok(!gff.includes('##FASTA\n')) // Invalid assembly - p = new Shell(`${apollo} export gff3 foobar`, false) + p = new Shell(`${apollo} export gff3 ${P} foobar`, false) assert.ok(p.returncode != 0) assert.ok(p.stderr.includes('foobar')) }) void globalThis.itName('Export gff3 from non-editable assembly', () => { new Shell( - `${apollo} assembly add-from-fasta test_data/tiny.fasta.gz -a vv1 -f`, + `${apollo} assembly add-from-fasta ${P} test_data/tiny.fasta.gz -a vv1 -f`, ) - new Shell(`${apollo} feature import test_data/tiny.fasta.gff3 -a vv1`) - let p = new Shell(`${apollo} export gff3 vv1 --include-fasta`) + new Shell(`${apollo} feature import ${P} test_data/tiny.fasta.gff3 -a vv1`) + let p = new Shell(`${apollo} export gff3 ${P} vv1 --include-fasta`) let gff = p.stdout assert.ok(gff.startsWith('##gff-version 3')) assert.ok(gff.includes('multivalue=val1,val2,val3')) @@ -1642,7 +1684,7 @@ EOF`, // We end with two newlines because the test data does have an extra newline at the end. assert.deepStrictEqual(gff.slice(-7), 'taccc\n\n') - p = new Shell(`${apollo} export gff3 vv1`) + p = new Shell(`${apollo} export gff3 ${P} vv1`) gff = p.stdout assert.ok(gff.startsWith('##gff-version 3')) assert.ok(gff.includes('multivalue=val1,val2,val3')) @@ -1651,10 +1693,10 @@ EOF`, void globalThis.itName('Export gff3 from external assembly', () => { new Shell( - `${apollo} assembly add-from-fasta http://localhost:3131/tiny.fasta.gz -a vv1 -f`, + `${apollo} assembly add-from-fasta ${P} http://localhost:3131/tiny.fasta.gz -a vv1 -f`, ) - new Shell(`${apollo} feature import test_data/tiny.fasta.gff3 -a vv1`) - let p = new Shell(`${apollo} export gff3 vv1 --include-fasta`) + new Shell(`${apollo} feature import ${P} test_data/tiny.fasta.gff3 -a vv1`) + let p = new Shell(`${apollo} export gff3 ${P} vv1 --include-fasta`) let gff = p.stdout assert.ok(gff.startsWith('##gff-version 3')) assert.ok(gff.includes('multivalue=val1,val2,val3')) @@ -1662,7 +1704,7 @@ EOF`, // We end with two newlines because the test data does have an extra newline at the end. assert.deepStrictEqual(gff.slice(-7), 'taccc\n\n') - p = new Shell(`${apollo} export gff3 vv1`) + p = new Shell(`${apollo} export gff3 ${P} vv1`) gff = p.stdout assert.ok(gff.startsWith('##gff-version 3')) assert.ok(gff.includes('multivalue=val1,val2,val3')) @@ -1673,12 +1715,12 @@ EOF`, 'Position of internal stop codon warning in forward', () => { new Shell( - `${apollo} assembly add-from-gff test_data/warningPositionForward.gff -a vv1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/warningPositionForward.gff -a vv1 -f`, ) - deleteAllChecks(apollo, 'vv1') - new Shell(`${apollo} assembly check -a vv1 -c CDSCheck`) + deleteAllChecks(apollo, P, 'vv1') + new Shell(`${apollo} assembly check ${P} -a vv1 -c CDSCheck`) - const p = new Shell(`${apollo} feature check -a vv1`) + const p = new Shell(`${apollo} feature check ${P} -a vv1`) const out = JSON.parse(p.stdout) assert.deepStrictEqual(out.length, 2) @@ -1696,11 +1738,11 @@ EOF`, 'Position of internal stop codon warning in reverse', () => { new Shell( - `${apollo} assembly add-from-gff test_data/warningPositionReverse.gff -a vv1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/warningPositionReverse.gff -a vv1 -f`, ) - deleteAllChecks(apollo, 'vv1') - new Shell(`${apollo} assembly check -a vv1 -c CDSCheck`) - const p = new Shell(`${apollo} feature check -a vv1`) + deleteAllChecks(apollo, P, 'vv1') + new Shell(`${apollo} assembly check ${P} -a vv1 -c CDSCheck`) + const p = new Shell(`${apollo} feature check ${P} -a vv1`) const out = JSON.parse(p.stdout) assert.deepStrictEqual(out.length, 2) assert.deepStrictEqual(out.at(0).cause, 'InternalStopCodon') @@ -1715,11 +1757,11 @@ EOF`, void globalThis.itName('Detect missing start codon forward', () => { new Shell( - `${apollo} assembly add-from-gff test_data/missingStartCodonForward.gff3 -a m1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/missingStartCodonForward.gff3 -a m1 -f`, ) - deleteAllChecks(apollo, 'm1') - new Shell(`${apollo} assembly check -a m1 -c CDSCheck`) - const p = new Shell(`${apollo} feature check -a m1`) + deleteAllChecks(apollo, P, 'm1') + new Shell(`${apollo} assembly check ${P} -a m1 -c CDSCheck`) + const p = new Shell(`${apollo} feature check ${P} -a m1`) const out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) assert.deepStrictEqual(out.at(0).cause, 'MissingStartCodon') @@ -1730,11 +1772,11 @@ EOF`, void globalThis.itName('Detect missing start codon reverse', () => { new Shell( - `${apollo} assembly add-from-gff test_data/missingStartCodonReverse.gff3 -a m1 -f`, + `${apollo} assembly add-from-gff ${P} test_data/missingStartCodonReverse.gff3 -a m1 -f`, ) - deleteAllChecks(apollo, 'm1') - new Shell(`${apollo} assembly check -a m1 -c CDSCheck`) - const p = new Shell(`${apollo} feature check -a m1`) + deleteAllChecks(apollo, P, 'm1') + new Shell(`${apollo} assembly check ${P} -a m1 -c CDSCheck`) + const p = new Shell(`${apollo} feature check ${P} -a m1`) const out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) assert.deepStrictEqual(out.at(0).cause, 'MissingStartCodon') @@ -1745,10 +1787,10 @@ EOF`, void globalThis.itName('Edit exon inferred from CDS', () => { new Shell( - `${apollo} assembly add-from-gff test_data/cdsWithoutExon.gff3 -f`, + `${apollo} assembly add-from-gff ${P} test_data/cdsWithoutExon.gff3 -f`, ) let p = new Shell( - `${apollo} feature search -t mrna01 -a cdsWithoutExon.gff3`, + `${apollo} feature search ${P} -t mrna01 -a cdsWithoutExon.gff3`, ) let out = JSON.parse(p.stdout) const gene: any = out.at(0) @@ -1759,27 +1801,27 @@ EOF`, const exon_id = exon[0]._id // Before edit - p = new Shell(`${apollo} feature get-id -i ${exon_id}`) + p = new Shell(`${apollo} feature get-id ${P} -i ${exon_id}`) out = JSON.parse(p.stdout) as AnnotationFeature[] assert.deepStrictEqual(out.at(0)?.max, 20) // After edit - new Shell(`${apollo} feature edit-coords -i ${exon_id} -e 30`) - p = new Shell(`${apollo} feature get-id -i ${exon_id}`) + new Shell(`${apollo} feature edit-coords ${P} -i ${exon_id} -e 30`) + p = new Shell(`${apollo} feature get-id ${P} -i ${exon_id}`) out = JSON.parse(p.stdout) as AnnotationFeature[] assert.deepStrictEqual(out.at(0)?.max, 30) }) void globalThis.itName('Check splice site', () => { new Shell( - `${apollo} assembly add-from-gff test_data/checkSplice.fasta.gff3 -f`, + `${apollo} assembly add-from-gff ${P} test_data/checkSplice.fasta.gff3 -f`, ) - deleteAllChecks(apollo, 'checkSplice.fasta.gff3') + deleteAllChecks(apollo, P, 'checkSplice.fasta.gff3') new Shell( - `${apollo} assembly check -a checkSplice.fasta.gff3 -c TranscriptCheck`, + `${apollo} assembly check ${P} -a checkSplice.fasta.gff3 -c TranscriptCheck`, ) - let p = new Shell(`${apollo} feature get -a checkSplice.fasta.gff3`) + let p = new Shell(`${apollo} feature get ${P} -a checkSplice.fasta.gff3`) const features = JSON.parse(p.stdout) const okMrnaId = [] @@ -1814,14 +1856,14 @@ EOF`, } p = new Shell( - `${apollo} feature check -a checkSplice.fasta.gff3 -i ${okMrnaId.join(' ')}`, + `${apollo} feature check ${P} -a checkSplice.fasta.gff3 -i ${okMrnaId.join(' ')}`, ) let out = JSON.parse(p.stdout) assert.deepStrictEqual(out, []) // Check forward transcript p = new Shell( - `${apollo} feature check -a checkSplice.fasta.gff3 -i ${warnMrnaIdForw}`, + `${apollo} feature check ${P} -a checkSplice.fasta.gff3 -i ${warnMrnaIdForw}`, ) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 4) @@ -1848,7 +1890,7 @@ EOF`, // Check reverse transcript p = new Shell( - `${apollo} feature check -a checkSplice.fasta.gff3 -i ${warnMrnaIdRev}`, + `${apollo} feature check ${P} -a checkSplice.fasta.gff3 -i ${warnMrnaIdRev}`, ) out = JSON.parse(p.stdout) assert.strictEqual(out.length, 4) diff --git a/packages/apollo-cli/src/test/utils.ts b/packages/apollo-cli/src/test/utils.ts index efd11e5d2..2c3bf37c0 100644 --- a/packages/apollo-cli/src/test/utils.ts +++ b/packages/apollo-cli/src/test/utils.ts @@ -26,11 +26,7 @@ export class Shell { constructor(cmd: string, strict = true, timeout?: number) { process.stdout.write(`${cmd}\n`) cmd = `set -e; set -u; set -o pipefail\n${cmd}` - const p = spawn.sync(cmd, { - shell: '/bin/bash', - timeout, - env: { APOLLO_PROFILE: 'testAdmin' }, - }) + const p = spawn.sync(cmd, { shell: '/bin/bash', timeout }) this.returncode = p.status this.stdout = p.stdout.toString() this.stderr = p.stderr.toString() @@ -42,11 +38,15 @@ export class Shell { } } -export function deleteAllChecks(apollo: string, assembly: string) { - const p = new Shell(`${apollo} assembly check`) +export function deleteAllChecks( + apollo: string, + profile: string, + assembly: string, +) { + const p = new Shell(`${apollo} assembly check ${profile}`) const out = JSON.parse(p.stdout) as CheckResultSnapshot[] const checks = out.map((chk) => chk.name) new Shell( - `${apollo} assembly check --assembly ${assembly} --delete --check ${checks.join(' ')}`, + `${apollo} assembly check ${profile} --assembly ${assembly} --delete --check ${checks.join(' ')}`, ) } From 80f0060fe8b9b591f754fc2ea7d476248786ca1c Mon Sep 17 00:00:00 2001 From: Garrett Stevens Date: Mon, 15 Dec 2025 23:19:59 +0000 Subject: [PATCH 4/4] Fix cases where indexed ID is modified or deleted --- packages/apollo-cli/src/test/test.ts | 84 +++++++++++++++++++ .../src/AssemblySpecificChange.ts | 16 +++- .../src/Changes/AddFeatureChange.ts | 6 +- .../src/Changes/DeleteFeatureChange.ts | 18 ++++ .../src/Changes/FeatureAttributeChange.ts | 13 +++ 5 files changed, 133 insertions(+), 4 deletions(-) diff --git a/packages/apollo-cli/src/test/test.ts b/packages/apollo-cli/src/test/test.ts index 76704f8d3..a3dc49896 100644 --- a/packages/apollo-cli/src/test/test.ts +++ b/packages/apollo-cli/src/test/test.ts @@ -846,6 +846,90 @@ void describe('Test CLI', () => { out = JSON.parse(p.stdout) assert.strictEqual(out.length, 1) assert.ok(out.at(0)?.type === 'gene') + + // Gets feature and child feature that were added manually (not imported) + p = new Shell( + `${apollo} feature add ${P} < { diff --git a/packages/apollo-common/src/AssemblySpecificChange.ts b/packages/apollo-common/src/AssemblySpecificChange.ts index 644a4e884..a167d95f6 100644 --- a/packages/apollo-common/src/AssemblySpecificChange.ts +++ b/packages/apollo-common/src/AssemblySpecificChange.ts @@ -1,5 +1,6 @@ /* eslint-disable @typescript-eslint/no-unnecessary-condition */ import { type AnnotationFeatureSnapshot } from '@apollo-annotation/mst' +import { type Feature } from '@apollo-annotation/schemas' import { Change, @@ -30,18 +31,27 @@ export abstract class AssemblySpecificChange extends Change { } getIndexedIds( - feature: AnnotationFeatureSnapshot, + feature: AnnotationFeatureSnapshot | Feature, idsToIndex: string[] | undefined, ): string[] { const indexedIds: string[] = [] for (const additionalId of idsToIndex ?? []) { - const idValue = feature.attributes?.[additionalId] + const { attributes } = feature + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment + const idValue: string[] = + attributes instanceof Map + ? attributes.get(additionalId) + : attributes?.[additionalId] if (idValue) { indexedIds.push(idValue[0]) } } if (feature.children) { - for (const child of Object.values(feature.children)) { + const childrenIterable = + feature.children instanceof Map + ? feature.children.values() + : Object.values(feature.children) + for (const child of childrenIterable) { const childIndexedIds = this.getIndexedIds(child, idsToIndex) indexedIds.push(...childIndexedIds) } diff --git a/packages/apollo-shared/src/Changes/AddFeatureChange.ts b/packages/apollo-shared/src/Changes/AddFeatureChange.ts index 2c743c550..814073749 100644 --- a/packages/apollo-shared/src/Changes/AddFeatureChange.ts +++ b/packages/apollo-shared/src/Changes/AddFeatureChange.ts @@ -123,6 +123,7 @@ export class AddFeatureChange extends FeatureChange { ) featureCnt++ } else { + const indexedIds = this.getIndexedIds(addedFeature, idsToIndex) // Adding new child feature if (parentFeatureId) { const topLevelFeature = await featureModel @@ -146,11 +147,14 @@ export class AddFeatureChange extends FeatureChange { this.addChild(parentFeature, addedFeature) const childIds = this.getChildFeatureIds(addedFeature) topLevelFeature.allIds.push(_id, ...childIds) + if (indexedIds.length > 0 && !topLevelFeature.indexedIds) { + topLevelFeature.indexedIds = [] + } + topLevelFeature.indexedIds?.push(...indexedIds) await topLevelFeature.save() } else { const childIds = this.getChildFeatureIds(addedFeature) const allIdsV2 = [_id, ...childIds] - const indexedIds = this.getIndexedIds(addedFeature, idsToIndex) const [newFeatureDoc] = await featureModel.create( [{ allIds: allIdsV2, indexedIds, status: 0, ...addedFeature }], { session }, diff --git a/packages/apollo-shared/src/Changes/DeleteFeatureChange.ts b/packages/apollo-shared/src/Changes/DeleteFeatureChange.ts index 6782f9366..3beda2091 100644 --- a/packages/apollo-shared/src/Changes/DeleteFeatureChange.ts +++ b/packages/apollo-shared/src/Changes/DeleteFeatureChange.ts @@ -67,6 +67,12 @@ export class DeleteFeatureChange extends FeatureChange { const { featureModel, session } = backend const { changes, logger } = this + const { INDEXED_IDS } = process.env + let idsToIndex: string[] | undefined + if (INDEXED_IDS) { + idsToIndex = INDEXED_IDS.split(',') + } + // Loop the changes for (const change of changes) { const { deletedFeature, parentFeatureId } = change @@ -105,6 +111,18 @@ export class DeleteFeatureChange extends FeatureChange { featureDoc.allIds = featureDoc.allIds.filter( (id) => !deletedIds.includes(id), ) + const indexedIds = this.getIndexedIds(featureDoc, idsToIndex) + if (featureDoc.indexedIds) { + if (indexedIds.length > 0) { + featureDoc.indexedIds = indexedIds + } else { + delete featureDoc.indexedIds + } + } else { + if (indexedIds.length > 0) { + featureDoc.indexedIds = indexedIds + } + } // Save updated document in Mongo featureDoc.markModified('children') // Mark as modified. Without this save() -method is not updating data in database try { diff --git a/packages/apollo-shared/src/Changes/FeatureAttributeChange.ts b/packages/apollo-shared/src/Changes/FeatureAttributeChange.ts index 48f7fb1a0..f2a2922a2 100644 --- a/packages/apollo-shared/src/Changes/FeatureAttributeChange.ts +++ b/packages/apollo-shared/src/Changes/FeatureAttributeChange.ts @@ -100,11 +100,24 @@ export class FeatureAttributeChange extends FeatureChange { featuresForChanges.push({ feature: foundFeature, topLevelFeature }) } + const { INDEXED_IDS } = process.env + let idsToIndex: string[] | undefined + if (INDEXED_IDS) { + idsToIndex = INDEXED_IDS.split(',') + } // Let's update objects for (const [idx, change] of changes.entries()) { const { newAttributes } = change const { feature, topLevelFeature } = featuresForChanges[idx] + const indexedIdsChanged = idsToIndex?.some( + (id) => id in newAttributes || id in (feature?.attributes ?? {}), + ) feature.attributes = newAttributes + if (indexedIdsChanged) { + const indexedIds = this.getIndexedIds(topLevelFeature, idsToIndex) + topLevelFeature.indexedIds = indexedIds + topLevelFeature.markModified('indexedIds') + } if (topLevelFeature._id.equals(feature._id)) { topLevelFeature.markModified('attributes') // Mark as modified. Without this save() -method is not updating data in database } else {