Skip to content

[Fix] nkey jetstream - #18

Merged
ndrean merged 1 commit into
g41797:mainfrom
ndrean:main
Dec 30, 2025
Merged

[Fix] nkey jetstream#18
ndrean merged 1 commit into
g41797:mainfrom
ndrean:main

Conversation

@ndrean

@ndrean ndrean commented Dec 30, 2025

Copy link
Copy Markdown
Collaborator

Addresses #17

Added JetStream tests in integration_test.zig.

@ndrean
ndrean requested a review from Copilot December 30, 2025 13:50
@ndrean
ndrean merged commit ad0bd33 into g41797:main Dec 30, 2025
8 checks passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a critical bug where NKey authentication failed for JetStream connections. The issue was caused by using an incorrect field name (nkey_pub instead of nkey) in the CONNECT message sent to the NATS server.

Key Changes:

  • Fixed the field name in ConnectMessage struct from nkey_pub to nkey to align with NATS protocol specifications
  • Added comprehensive JetStream integration tests for all authentication methods (token, user/pass, nkey) to prevent similar issues
  • Fixed memory leaks in consumer tests by properly placing defer js.DISCONNECT() statements

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.

File Description
src/protocol.zig Corrected the NKey field name from nkey_pub to nkey in the ConnectMessage struct, enabling proper NKey authentication for JetStream
src/integration_tests.zig Added JetStream test coverage for token, user/pass, and nkey authentication methods to verify end-to-end authentication flows
src/consumer_tests.zig Fixed memory leaks by moving defer js.DISCONNECT() immediately after JetStream connection and simplified error handling
.github/workflows/ci.yml Reorganized test execution order and updated NATS server ports to prevent conflicts between different test stages

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/integration_tests.zig
Comment on lines +78 to +92
var stream_created = false;
js.CREATE(&stream_config) catch |err| {
if (err == error.StreamAlreadyExists) {
std.debug.print("Token auth (JetStream): Stream already exists (OK)\n", .{});
stream_created = true;
} else {
std.debug.print("Token auth (JetStream): CREATE failed with error: {}\n", .{err});
return err;
}
};

if (!stream_created) {
stream_created = true;
}

Copilot AI Dec 30, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stream_created variable and its associated logic appear redundant. The variable is initialized to false, set to true in the error handler if the stream already exists, and then unconditionally set to true afterwards (lines 89-91) regardless of whether the CREATE succeeded or failed. Since the variable isn't used for any decision-making or validation, this logic can be removed entirely. The error handling for CREATE already properly handles the StreamAlreadyExists case.

Suggested change
var stream_created = false;
js.CREATE(&stream_config) catch |err| {
if (err == error.StreamAlreadyExists) {
std.debug.print("Token auth (JetStream): Stream already exists (OK)\n", .{});
stream_created = true;
} else {
std.debug.print("Token auth (JetStream): CREATE failed with error: {}\n", .{err});
return err;
}
};
if (!stream_created) {
stream_created = true;
}
js.CREATE(&stream_config) catch |err| {
if (err == error.StreamAlreadyExists) {
std.debug.print("Token auth (JetStream): Stream already exists (OK)\n", .{});
} else {
std.debug.print("Token auth (JetStream): CREATE failed with error: {}\n", .{err});
return err;
}
};

Copilot uses AI. Check for mistakes.
Comment thread src/integration_tests.zig
Comment on lines +152 to +166
var stream_created = false;
js.CREATE(&stream_config) catch |err| {
if (err == error.StreamAlreadyExists) {
std.debug.print("User/Pass auth (JetStream): Stream already exists (OK)\n", .{});
stream_created = true;
} else {
std.debug.print("User/Pass auth (JetStream): CREATE failed with error: {}\n", .{err});
return err;
}
};

if (!stream_created) {
stream_created = true;
}

Copilot AI Dec 30, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stream_created variable and its associated logic appear redundant. The variable is initialized to false, set to true in the error handler if the stream already exists, and then unconditionally set to true afterwards (lines 163-165) regardless of whether the CREATE succeeded or failed. Since the variable isn't used for any decision-making or validation, this logic can be removed entirely. The error handling for CREATE already properly handles the StreamAlreadyExists case.

Suggested change
var stream_created = false;
js.CREATE(&stream_config) catch |err| {
if (err == error.StreamAlreadyExists) {
std.debug.print("User/Pass auth (JetStream): Stream already exists (OK)\n", .{});
stream_created = true;
} else {
std.debug.print("User/Pass auth (JetStream): CREATE failed with error: {}\n", .{err});
return err;
}
};
if (!stream_created) {
stream_created = true;
}
js.CREATE(&stream_config) catch |err| {
if (err == error.StreamAlreadyExists) {
std.debug.print("User/Pass auth (JetStream): Stream already exists (OK)\n", .{});
} else {
std.debug.print("User/Pass auth (JetStream): CREATE failed with error: {}\n", .{err});
return err;
}
};

Copilot uses AI. Check for mistakes.
Comment thread src/integration_tests.zig
Comment on lines +230 to +245
var stream_created = false;
js.CREATE(&stream_config) catch |err| {
// If stream already exists, that's fine - we can still test INFO
if (err == error.StreamAlreadyExists) {
std.debug.print("NKey auth (JetStream): Stream already exists (OK)\n", .{});
stream_created = true;
} else {
// For any other error, this is a real failure
std.debug.print("NKey auth (JetStream): CREATE failed with error: {}\n", .{err});
return err;
}
};

if (!stream_created) {
stream_created = true;
}

Copilot AI Dec 30, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stream_created variable and its associated logic appear redundant. The variable is initialized to false, set to true in the error handler if the stream already exists, and then unconditionally set to true afterwards (lines 243-245) regardless of whether the CREATE succeeded or failed. Since the variable isn't used for any decision-making or validation, this logic can be removed entirely. The error handling for CREATE already properly handles the StreamAlreadyExists case.

Copilot uses AI. Check for mistakes.
Comment thread src/integration_tests.zig
Comment on lines +59 to +101
// Test 2: JetStream operations
{
const JetStream = @import("JetStream.zig");

const js_opts: protocol.ConnectOpts = .{
.addr = "127.0.0.1",
.port = 4223,
.auth_token = token,
};

var js = try JetStream.CONNECT(allocator, js_opts);
defer js.DISCONNECT();

const test_stream_name = "TOKEN_AUTH_TEST";
var stream_config: protocol.StreamConfig = .{
.name = test_stream_name,
.subjects = &.{"token.test.>"},
};

var stream_created = false;
js.CREATE(&stream_config) catch |err| {
if (err == error.StreamAlreadyExists) {
std.debug.print("Token auth (JetStream): Stream already exists (OK)\n", .{});
stream_created = true;
} else {
std.debug.print("Token auth (JetStream): CREATE failed with error: {}\n", .{err});
return err;
}
};

if (!stream_created) {
stream_created = true;
}

const empty_request: JetStream.StreamInfoRequest = .{};
const info = try js.INFO(test_stream_name, &empty_request);

std.debug.print("Token auth (JetStream): ✅ Stream INFO succeeded - {d} messages\n", .{info.state.?.messages});

js.DELETE(test_stream_name) catch |err| {
std.debug.print("Token auth (JetStream): DELETE failed (OK): {}\n", .{err});
};
}

Copilot AI Dec 30, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The JetStream test logic is duplicated across three authentication tests (token, user/pass, and nkey). Consider extracting this common test logic into a helper function that accepts the connection options as a parameter. This would reduce duplication and make the tests easier to maintain. The only differences between these blocks are the connection options and the stream/subject names used.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants