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,