diff --git a/src/scripts/streams/BuildAntibodiesStream.test.js b/src/scripts/streams/BuildAntibodiesStream.test.js new file mode 100644 index 0000000000000000000000000000000000000000..45cb0dc45f971e51ac7ad880a488ba14b3dd8f31 --- /dev/null +++ b/src/scripts/streams/BuildAntibodiesStream.test.js @@ -0,0 +1,130 @@ +// 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 { BuildAntibodiesStream } from "./BuildAntibodiesStream.js" +import { PassThrough } from "node:stream" +import assert from "node:assert" + +describe('BuildAntibodiesStream', () => { + it('should be in object mode', () => { + const buildAntibodies = new BuildAntibodiesStream() + assert( + buildAntibodies.readableObjectMode === true, + 'The objectMode parameter should be true' + ) + }) + + it('should accept a species as argument', () => { + const buildAntibodies = new BuildAntibodiesStream({ species: 'Homo sapiens' }) + assert( + buildAntibodies.species === 'Homo sapiens', + 'The species property should be set by passing an argument' + ) + }) + + it('should have a lightChain property, set to null', () => { + const buildAntibodies = new BuildAntibodiesStream() + assert( + buildAntibodies.lightChain === null, + 'The lightChain property needs to be set' + ) + }) + + it('should build an antibody object from two fasta objects, light and heavy chains', (test, done) => { + const inputLightChain = { + header: 'lightID|||firstLightHeader;firstLightID;firstLightSource;firstLightSegment|||secondLightHeader;secondLightID;secondLightSource;secondLightSegment', + sequence: 'LIGHTCHAINSEQUENCE' + } + + const inputHeavyChain = { + header: 'heavyID|||firstHeavyHeader;firstHeavyID;firstHeavySource;firstHeavySegment|||secondHeavyHeader;secondHeavyID;secondHeavySource;secondHeavySegment', + sequence: 'HEAVYCHAINSEQUENCE' + } + + const inputSpecies = 'Homo sapiens' + + const expected = { + id: 'lightID', + species: inputSpecies, + lightChain: { + headers: [ + { + header: 'firstLightHeader', + id: 'firstLightID', + source: 'firstLightSegment', + vGeneSegment: 'firstLightSource' + }, + { + header: 'secondLightHeader', + id: 'secondLightID', + source: 'secondLightSegment', + vGeneSegment: 'secondLightSource' + } + ], + rawFasta: inputLightChain.header, + sequence: 'LIGHTCHAINSEQUENCE', + }, + heavyChain: { + headers: [ + { + header: 'firstHeavyHeader', + id: 'firstHeavyID', + source: 'firstHeavySegment', + vGeneSegment: 'firstHeavySource' + }, + { + header: 'secondHeavyHeader', + id: 'secondHeavyID', + source: 'secondHeavySegment', + vGeneSegment: 'secondHeavySource' + } + ], + rawFasta: inputHeavyChain.header, + sequence: 'HEAVYCHAINSEQUENCE' + } + } + + // Setup a test pipeline with mock-up streams + const inputStream = new PassThrough({ objectMode: true }) + const outputStream = new PassThrough({ objectMode: true }) + const buildAntibodies = new BuildAntibodiesStream({ species: inputSpecies }) + + inputStream.pipe(buildAntibodies).pipe(outputStream) + + // Handle processed data on the output stream + let outputData = null + outputStream.on('data', (chunk) => { + outputData = chunk + }) + + // Assert at the end of the stream + outputStream.on('end', () => { + assert.deepStrictEqual(outputData, expected) + assert( + buildAntibodies.lightChain === null, + 'The lightChain property should be reset to null after an antibody is emitted' + ) + + done() + }) + + // Start the process + inputStream.write(inputLightChain) + inputStream.write(inputHeavyChain) + inputStream.end() + }) +}) diff --git a/src/scripts/streams/GenerateHashIdStream.test.js b/src/scripts/streams/GenerateHashIdStream.test.js new file mode 100644 index 0000000000000000000000000000000000000000..8fb35c4fcbab7a0b6685f2dc043fc126eaae266e --- /dev/null +++ b/src/scripts/streams/GenerateHashIdStream.test.js @@ -0,0 +1,76 @@ +// 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 { GenerateHashIdStream } from "./GenerateHashIdStream.js" +import { PassThrough } from "node:stream" +import assert from "node:assert" + +describe('GenerateHashIdStream', () => { + it('should be in object mode', () => { + const parser = new GenerateHashIdStream() + assert( + parser.readableObjectMode === true, + 'The objectMode parameter should be true' + ) + }) + + it('should remove extra spaces and tabs from a fasta object', (test, done) => { + const inputAntibody = { + species: 'Homo sapiens', + lightChain: { + sequence: 'LIGHTCHAINSEQUENCE', + }, + heavyChain: { + sequence: 'HEAVYCHAINSEQUENCE' + } + } + + const expected = { + hashId: "164a3b9b60621d13a0f25ed56c2932e4aca79208a4a01d6d886507636ae059ad", + species: 'Homo sapiens', + lightChain: { + sequence: 'LIGHTCHAINSEQUENCE', + }, + heavyChain: { + sequence: 'HEAVYCHAINSEQUENCE' + } + } + + // Setup a test pipeline with mock-up streams + const inputStream = new PassThrough({ objectMode: true }) + const outputStream = new PassThrough({ objectMode: true }) + const parser = new GenerateHashIdStream() + + 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(inputAntibody) + inputStream.end() + }) +}) diff --git a/src/scripts/streams/GroupAntibodiesStream.test.js b/src/scripts/streams/GroupAntibodiesStream.test.js new file mode 100644 index 0000000000000000000000000000000000000000..0596eee344cf774e1193fe66dc1fe62f52b9427c --- /dev/null +++ b/src/scripts/streams/GroupAntibodiesStream.test.js @@ -0,0 +1,71 @@ +// 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 { GroupAntibodiesStream } from "./GroupAntibodiesStream.js" +import { PassThrough } from "node:stream" +import assert from "node:assert" + +describe('GroupAntibodiesStream', () => { + it('should be in object mode', () => { + const groupAntibodies = new GroupAntibodiesStream() + assert.ok(groupAntibodies.readableObjectMode) + }) + + it('should have a group property set to an empty array', () => { + const groupAntibodies = new GroupAntibodiesStream() + assert.deepStrictEqual(groupAntibodies.group, []) + }) + + it('should accept a group size as an argument', () => { + const groupSize = 500 + const groupAntibodies = new GroupAntibodiesStream({ groupSize }) + assert.strictEqual(groupAntibodies.groupSize, groupSize) + }) + + it('should group antibodies and emit an array when the given group size is reached', (test, done) => { + const antibodiesNumber = 1357 + const groupSize = 500 + + // Setup a test pipeline with mock-up streams + const inputStream = new PassThrough({ objectMode: true }) + const outputStream = new PassThrough({ objectMode: true }) + const groupAntibodies = new GroupAntibodiesStream({ groupSize }) + + inputStream.pipe(groupAntibodies).pipe(outputStream) + + // Handle processed data on the output stream + let outputGroups = [] + outputStream.on('data', (group) => { + outputGroups.push(group) + }) + + // Assert at the end of the stream + outputStream.on('end', () => { + assert.strictEqual(outputGroups[0].length, 500) + assert.strictEqual(outputGroups[1].length, 500) + assert.strictEqual(outputGroups[2].length, 357, "The last group should contain the remaining antibodies") + done() + }) + + // Start the process + for (let i = 0; i < antibodiesNumber; i++) { + inputStream.write({}) + } + + inputStream.end() + }) +}) diff --git a/src/scripts/streams/ParseFastaStream.test.js b/src/scripts/streams/ParseFastaStream.test.js index e2b831bbcbaf6c9550e29101804ef1b0264bfe10..4eb5c280f8501da67d85bc03295e873cf3880ac2 100644 --- a/src/scripts/streams/ParseFastaStream.test.js +++ b/src/scripts/streams/ParseFastaStream.test.js @@ -20,11 +20,11 @@ import { PassThrough } from "node:stream" import assert from "node:assert" describe('ParseFastaStream', () => { - it('should be in object mode by default', () => { + it('should be in object mode', () => { const parser = new ParseFastaStream() assert( parser.readableObjectMode === true, - 'The objectMode parameter should be true by default' + 'The objectMode parameter should be true' ) }) diff --git a/src/scripts/streams/SanitizeFastaStream.test.js b/src/scripts/streams/SanitizeFastaStream.test.js index c3a647b9420bfe0bbe62ab68c33f402680a7cb0a..93b6beecc3621d6282355ce33b6fa01d19a30944 100644 --- a/src/scripts/streams/SanitizeFastaStream.test.js +++ b/src/scripts/streams/SanitizeFastaStream.test.js @@ -20,11 +20,11 @@ import { PassThrough } from "node:stream" import assert from "node:assert" describe('SanitizeFastaStream', () => { - it('should be in object mode by default', () => { + it('should be in object mode', () => { const parser = new SanitizeFastaStream() assert( parser.readableObjectMode === true, - 'The objectMode parameter should be true by default' + 'The objectMode parameter should be true' ) }) @@ -33,6 +33,7 @@ describe('SanitizeFastaStream', () => { header: 'this is \t the header\t', sequence: ' THISISTHESEQUENCE ' } + const expected = { header: 'this is the header', sequence: 'THISISTHESEQUENCE' diff --git a/src/scripts/streams/SplitFastaStream.test.js b/src/scripts/streams/SplitFastaStream.test.js index f5ec9ebb54e6b1234b3f3c6a9ed9921a06b81d0d..2d8f96c1fadfcb3be65d4fdb0eaa7a383560e242 100644 --- a/src/scripts/streams/SplitFastaStream.test.js +++ b/src/scripts/streams/SplitFastaStream.test.js @@ -20,15 +20,15 @@ import { PassThrough } from "node:stream" import assert from "node:assert" describe('SplitFastaStream', () => { - it('should be in object mode by default', () => { + it('should be in object mode', () => { const splitFasta = new SplitFastaStream() assert( splitFasta.readableObjectMode === true, - 'The objectMode parameter should be true by default' + 'The objectMode parameter should be true' ) }) - it('should have a buffer property, intialized as an empty string', () => { + it('should have a buffer property, set as an empty string', () => { const splitFasta = new SplitFastaStream() assert( splitFasta.buffer === '', @@ -36,7 +36,7 @@ describe('SplitFastaStream', () => { ) }) - it('should have a firstLine property, intialized to true', () => { + it('should have a firstLine property, set to true', () => { const splitFasta = new SplitFastaStream() assert( splitFasta.firstLine === true,