diff --git a/package.json b/package.json index 36ec437e3eab1f0cec653116792a87b037d14d4d..ec1f0a82f15a6afb559ae9d4dfe11d1c352906a7 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "type": "module", "scripts": { "start": "node src/server/app.js", + "test": "node --test", "dev": "concurrently -n Client,Server -c '#325D79,#45ADA8' 'npm:dev:client' 'npm:dev:server'", "dev:client": "vite", "dev:server": "nodemon --use-strict src/server/app.js | pino-pretty -t 'yyyy-mm-dd HH:MM:ss'", diff --git a/src/scripts/streams/ParseFastaStream.test.js b/src/scripts/streams/ParseFastaStream.test.js new file mode 100644 index 0000000000000000000000000000000000000000..e2b831bbcbaf6c9550e29101804ef1b0264bfe10 --- /dev/null +++ b/src/scripts/streams/ParseFastaStream.test.js @@ -0,0 +1,61 @@ +// ABSD +// Copyright (C) 2025 Institut Pasteur +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +import { describe, it } from "node:test" +import { ParseFastaStream } from "./ParseFastaStream.js" +import { PassThrough } from "node:stream" +import assert from "node:assert" + +describe('ParseFastaStream', () => { + it('should be in object mode by default', () => { + const parser = new ParseFastaStream() + assert( + parser.readableObjectMode === true, + 'The objectMode parameter should be true by default' + ) + }) + + it('should transform a raw fasta entry into an object', (test, done) => { + const inputData = 'this is the header\nTHISISTHESEQUENCE' + const expected = { + header: 'this is the header', + sequence: 'THISISTHESEQUENCE' + } + + // Setup a test pipeline with mock-up streams + const inputStream = new PassThrough({ objectMode: true }) + const outputStream = new PassThrough({ objectMode: true }) + const parser = new ParseFastaStream() + + inputStream.pipe(parser).pipe(outputStream) + + // Handle processed data on the output stream + let outputData = '' + outputStream.on('data', (chunk) => { + outputData = chunk + }) + + // Assert at the end of the stream + outputStream.on('end', () => { + assert.deepStrictEqual(outputData, expected) + done() + }) + + // Start the process + inputStream.write(inputData) + inputStream.end() + }) +}) diff --git a/src/scripts/streams/SanitizeFastaStream.test.js b/src/scripts/streams/SanitizeFastaStream.test.js new file mode 100644 index 0000000000000000000000000000000000000000..c3a647b9420bfe0bbe62ab68c33f402680a7cb0a --- /dev/null +++ b/src/scripts/streams/SanitizeFastaStream.test.js @@ -0,0 +1,64 @@ +// ABSD +// Copyright (C) 2025 Institut Pasteur +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +import { describe, it } from "node:test" +import { SanitizeFastaStream } from "./SanitizeFastaStream.js" +import { PassThrough } from "node:stream" +import assert from "node:assert" + +describe('SanitizeFastaStream', () => { + it('should be in object mode by default', () => { + const parser = new SanitizeFastaStream() + assert( + parser.readableObjectMode === true, + 'The objectMode parameter should be true by default' + ) + }) + + it('should remove extra spaces and tabs from a fasta object', (test, done) => { + const inputData = { + header: 'this is \t the header\t', + sequence: ' THISISTHESEQUENCE ' + } + const expected = { + header: 'this is the header', + sequence: 'THISISTHESEQUENCE' + } + + // Setup a test pipeline with mock-up streams + const inputStream = new PassThrough({ objectMode: true }) + const outputStream = new PassThrough({ objectMode: true }) + const parser = new SanitizeFastaStream() + + inputStream.pipe(parser).pipe(outputStream) + + // Handle processed data on the output stream + let outputData = '' + outputStream.on('data', (chunk) => { + outputData = chunk + }) + + // Assert at the end of the stream + outputStream.on('end', () => { + assert.deepStrictEqual(outputData, expected) + done() + }) + + // Start the process + inputStream.write(inputData) + inputStream.end() + }) +}) diff --git a/src/scripts/streams/SplitFastaStream.test.js b/src/scripts/streams/SplitFastaStream.test.js new file mode 100644 index 0000000000000000000000000000000000000000..f5ec9ebb54e6b1234b3f3c6a9ed9921a06b81d0d --- /dev/null +++ b/src/scripts/streams/SplitFastaStream.test.js @@ -0,0 +1,83 @@ +// ABSD +// Copyright (C) 2025 Institut Pasteur +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see <http://www.gnu.org/licenses/>. + +import { describe, it } from "node:test" +import { SplitFastaStream } from "./SplitFastaStream.js" +import { PassThrough } from "node:stream" +import assert from "node:assert" + +describe('SplitFastaStream', () => { + it('should be in object mode by default', () => { + const splitFasta = new SplitFastaStream() + assert( + splitFasta.readableObjectMode === true, + 'The objectMode parameter should be true by default' + ) + }) + + it('should have a buffer property, intialized as an empty string', () => { + const splitFasta = new SplitFastaStream() + assert( + splitFasta.buffer === '', + 'The buffer property needs to be set' + ) + }) + + it('should have a firstLine property, intialized to true', () => { + const splitFasta = new SplitFastaStream() + assert( + splitFasta.firstLine === true, + 'The firstLine property needs to be set' + ) + }) + + it('should decode and split a buffer into a series of raw fasta, sent one by one', (test, done) => { + const inputData = new Buffer.from( + '>First header\nFIRSTSEQUENCE\n' + + '>Second header\nSECONDSEQUENCE\n' + + '>Third header\nTHIRDSEQUENCE\n' + ) + + const expected = [ + 'First header\nFIRSTSEQUENCE\n', + 'Second header\nSECONDSEQUENCE\n', + 'Third header\nTHIRDSEQUENCE\n' + ] + + // Setup a test pipeline with mock-up streams + const inputStream = new PassThrough() + const outputStream = new PassThrough({ objectMode: true }) + const splitFasta = new SplitFastaStream() + + inputStream.pipe(splitFasta).pipe(outputStream) + + // Handle processed data on the output stream + let outputData = [] + outputStream.on('data', (chunk) => { + outputData.push(chunk) + }) + + // Assert at the end of the stream + outputStream.on('end', () => { + assert.deepStrictEqual(outputData, expected) + done() + }) + + // Start the process + inputStream.write(inputData) + inputStream.end() + }) +})