| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| // |
| |
| // TLS integration tests for the Node.js SDK. |
| // |
| // These tests require a TLS-enabled Iggy server and are skipped by default. |
| // To run them locally: |
| // |
| // 1. Start the server with TLS: |
| // IGGY_ROOT_USERNAME=iggy IGGY_ROOT_PASSWORD=iggy \ |
| // IGGY_TCP_TLS_ENABLED=true \ |
| // IGGY_TCP_TLS_CERT_FILE=core/certs/iggy_cert.pem \ |
| // IGGY_TCP_TLS_KEY_FILE=core/certs/iggy_key.pem \ |
| // cargo r --bin iggy-server |
| // |
| // 2. Run the tests: |
| // cd foreign/node |
| // IGGY_TCP_TLS_ENABLED=true IGGY_TCP_ADDRESS=127.0.0.1:8090 \ |
| // node --import @swc-node/register/esm-register --test src/e2e/tls.system.e2e.ts |
| |
| import { readFileSync } from 'node:fs'; |
| import { resolve } from 'node:path'; |
| import { after, describe, it } from 'node:test'; |
| import assert from 'node:assert/strict'; |
| import { Client } from '../client/client.js'; |
| import { Partitioning, Consumer, PollingStrategy } from '../wire/index.js'; |
| import { getIggyAddress } from '../tcp.sm.utils.js'; |
| |
| const tlsEnabled = process.env.IGGY_TCP_TLS_ENABLED === 'true'; |
| |
| // Path to the CA certificate. Override with E2E_ROOT_CA_CERT env var, |
| // otherwise fall back to the default relative path from the repo root. |
| const caCertPath = process.env.E2E_ROOT_CA_CERT |
| || resolve(process.cwd(), '../../core/certs/iggy_ca_cert.pem'); |
| |
| const getTlsClient = () => { |
| const [, port] = getIggyAddress(); |
| const caCert = readFileSync(caCertPath); |
| |
| // The server certificate SAN is DNS:localhost, so we connect via 'localhost' |
| // for proper hostname verification (consistent with Python and C# TLS tests). |
| return new Client({ |
| transport: 'TLS', |
| options: { |
| port, |
| host: 'localhost', |
| ca: caCert, |
| }, |
| credentials: { username: 'iggy', password: 'iggy' }, |
| }); |
| }; |
| |
| describe('e2e -> tls', { skip: !tlsEnabled && 'IGGY_TCP_TLS_ENABLED is not set' }, async () => { |
| |
| const c = getTlsClient(); |
| const credentials = { username: 'iggy', password: 'iggy' }; |
| |
| it('e2e -> tls::ping', async () => { |
| assert.ok(await c.system.ping()); |
| }); |
| |
| it('e2e -> tls::login', async () => { |
| assert.deepEqual( |
| await c.session.login(credentials), |
| { userId: 0 } |
| ); |
| }); |
| |
| it('e2e -> tls::getStats', async () => { |
| const stats = await c.system.getStats(); |
| assert.ok(stats); |
| assert.ok('processId' in stats); |
| assert.ok('hostname' in stats); |
| }); |
| |
| it('e2e -> tls::send and poll messages', async () => { |
| const streamName = 'tls-e2e-stream'; |
| const topicName = 'tls-e2e-topic'; |
| |
| await c.stream.create({ name: streamName }); |
| await c.topic.create({ |
| streamId: streamName, |
| name: topicName, |
| partitionCount: 1, |
| compressionAlgorithm: 1, |
| }); |
| |
| const messages = [ |
| { id: 1, headers: [], payload: 'tls-message-1' }, |
| { id: 2, headers: [], payload: 'tls-message-2' }, |
| { id: 3, headers: [], payload: 'tls-message-3' }, |
| ]; |
| |
| await c.message.send({ |
| streamId: streamName, |
| topicId: topicName, |
| messages, |
| partition: Partitioning.PartitionId(0), |
| }); |
| |
| const polled = await c.message.poll({ |
| streamId: streamName, |
| topicId: topicName, |
| consumer: Consumer.Single, |
| partitionId: 0, |
| pollingStrategy: PollingStrategy.First, |
| count: 10, |
| autocommit: false, |
| }); |
| |
| assert.equal(polled.messages.length, 3); |
| |
| await c.stream.delete({ streamId: streamName }); |
| }); |
| |
| it('e2e -> tls::logout', async () => { |
| assert.ok(await c.session.logout()); |
| }); |
| |
| after(() => { |
| c.destroy(); |
| }); |
| }); |