Compare commits
4 Commits
60632f62ad
...
cba76ae724
Author | SHA1 | Date | |
---|---|---|---|
cba76ae724 | |||
1c3e5ff538 | |||
d3d5849f55 | |||
4cf5049882 |
@ -19,6 +19,7 @@ pub fn build(b: *std.Build) void {
|
||||
});
|
||||
|
||||
const tests = b.addTest(.{
|
||||
.name = "nats-zig-unit-tests",
|
||||
.root_source_file = .{ .path = "tests/main.zig" },
|
||||
.target = target,
|
||||
.optimize = optimize,
|
||||
@ -29,6 +30,7 @@ pub fn build(b: *std.Build) void {
|
||||
|
||||
const run_main_tests = b.addRunArtifact(tests);
|
||||
const test_step = b.step("test", "Run tests");
|
||||
test_step.dependOn(&b.addInstallArtifact(tests, .{}).step);
|
||||
test_step.dependOn(&run_main_tests.step);
|
||||
|
||||
add_examples(b, .{
|
||||
|
@ -12,7 +12,7 @@ pub const ErrorInfo = struct {
|
||||
};
|
||||
|
||||
pub fn getLastError() ErrorInfo {
|
||||
const status: c_uint = 0;
|
||||
var status: c_uint = 0;
|
||||
const desc = nats_c.nats_GetLastError(&status);
|
||||
|
||||
return .{
|
||||
@ -21,8 +21,8 @@ pub fn getLastError() ErrorInfo {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn getLastErrorStack(buffer: []u8) Error!void {
|
||||
const status = Status.fromInt(nats_c.getLastErrorStack(buffer.ptr, buffer.len));
|
||||
pub fn getLastErrorStack(buffer: *[]u8) Error!void {
|
||||
const status = Status.fromInt(nats_c.nats_GetLastErrorStack(buffer.ptr, buffer.len));
|
||||
return status.raise();
|
||||
}
|
||||
|
||||
|
69
src/nats.zig
69
src/nats.zig
@ -35,6 +35,9 @@ pub const Message = msg_.Message;
|
||||
pub const Statistics = sta_.Statistics;
|
||||
pub const StatsCounts = sta_.StatsCounts;
|
||||
|
||||
pub const ErrorInfo = err_.ErrorInfo;
|
||||
pub const getLastError = err_.getLastError;
|
||||
pub const getLastErrorStack = err_.getLastErrorStack;
|
||||
pub const Status = err_.Status;
|
||||
pub const Error = err_.Error;
|
||||
|
||||
@ -59,7 +62,7 @@ pub fn now() i64 {
|
||||
return nats_c.nats_Now();
|
||||
}
|
||||
|
||||
pub fn nowInNanoSeconds() i64 {
|
||||
pub fn nowInNanoseconds() i64 {
|
||||
return nats_c.nats_NowInNanoSeconds();
|
||||
}
|
||||
|
||||
@ -76,6 +79,8 @@ pub fn releaseThreadMemory() void {
|
||||
return nats_c.nats_ReleaseThreadMemory();
|
||||
}
|
||||
|
||||
pub const default_spin_count: i64 = -1;
|
||||
|
||||
pub fn init(lock_spin_count: i64) Error!void {
|
||||
const status = Status.fromInt(nats_c.nats_Open(lock_spin_count));
|
||||
return status.raise();
|
||||
@ -85,40 +90,52 @@ pub fn deinit() void {
|
||||
return nats_c.nats_Close();
|
||||
}
|
||||
|
||||
// the result of this requires manual deallocation unless it is used to provide the
|
||||
// signature out-parameter in the natsSignatureHandler callback. Calling it outside of
|
||||
// that context seems unlikely, but we should probably provide a deinit function so the
|
||||
// user doesn't have to dig around for libc free to deallocate it.
|
||||
pub fn sign(encoded_seed: [:0]const u8, input: [:0]const u8) Error![]const u8 {
|
||||
var result: [*]u8 = undefined;
|
||||
var length: c_int = 0;
|
||||
const status = Status.fromInt(nats_c.nats_Sign(encoded_seed.ptr, &input, &length));
|
||||
|
||||
return status.toError() orelse result[0..@intCast(length)];
|
||||
}
|
||||
|
||||
pub fn deinitWait(timeout: i64) Error!void {
|
||||
const status = Status.fromInt(nats_c.nats_CloseAndWait(timeout));
|
||||
return status.raise();
|
||||
}
|
||||
|
||||
// This appears to be a jetstream API, but these two endpoints are trivial, so, whoops.
|
||||
// I have no clue what this does, since there's basically no
|
||||
pub const Inbox = opaque {
|
||||
pub fn create() Error!*Inbox {
|
||||
var self: *Inbox = undefined;
|
||||
const status = Status.fromInt(nats_c.natsInbox_Create(@ptrCast(&self)));
|
||||
// the result of this requires manual deallocation unless it is used to provide the
|
||||
// signature out-parameter in the natsSignatureHandler callback. Calling it outside of
|
||||
// that context seems unlikely, but we should probably provide a deinit function so the
|
||||
// user doesn't have to dig around for libc free to deallocate it.
|
||||
pub fn sign(encoded_seed: [:0]const u8, input: [:0]const u8) Error![]const u8 {
|
||||
var result: [*c]u8 = undefined;
|
||||
var length: c_int = 0;
|
||||
const status = Status.fromInt(nats_c.nats_Sign(
|
||||
encoded_seed.ptr,
|
||||
input.ptr,
|
||||
&result,
|
||||
&length,
|
||||
));
|
||||
|
||||
return status.toError() orelse self;
|
||||
}
|
||||
return status.toError() orelse result[0..@intCast(length)];
|
||||
}
|
||||
|
||||
pub fn destroy(self: *Inbox) void {
|
||||
nats_c.natsInbox_Destroy(@ptrCast(self));
|
||||
}
|
||||
};
|
||||
// Note: an "Inbox" is actually just a string. This API creates a random (unique)
|
||||
// string suitable for passing as the `reply` field to Message.create or
|
||||
// Connection.publishRequest. The string is owned by the caller and should be freed
|
||||
// using `destroyInbox`.
|
||||
pub fn createInbox() Error![:0]u8 {
|
||||
var self: [*c]u8 = undefined;
|
||||
const status = Status.fromInt(nats_c.natsInbox_Create(@ptrCast(&self)));
|
||||
|
||||
return status.toError() orelse std.mem.sliceTo(self, 0);
|
||||
}
|
||||
|
||||
pub fn destroyInbox(inbox: [:0]u8) void {
|
||||
nats_c.natsInbox_Destroy(@ptrCast(inbox.ptr));
|
||||
}
|
||||
|
||||
// I think this is also a jetstream API. This function sure does not seem at all useful
|
||||
// by itself.
|
||||
// by itself. Note: for some reason, most of the jetstream data structures are all
|
||||
// public, instead of following the opaque handle style that the rest of the library
|
||||
// does.
|
||||
|
||||
// typedef struct natsMsgList {
|
||||
// natsMsg **Msgs;
|
||||
// int Count;
|
||||
// } natsMsgList;
|
||||
pub const MessageList = opaque {
|
||||
pub fn destroy(self: *MessageList) void {
|
||||
nats_c.natsMsgList_Destroy(@ptrCast(self));
|
||||
|
18
tests/connection.zig
Normal file
18
tests/connection.zig
Normal file
@ -0,0 +1,18 @@
|
||||
const std = @import("std");
|
||||
|
||||
const nats = @import("nats");
|
||||
|
||||
const util = @import("./util.zig");
|
||||
|
||||
test "nats.Connection.connectTo" {
|
||||
var server = try util.TestServer.launch(.{});
|
||||
defer server.stop();
|
||||
|
||||
{
|
||||
try nats.init(nats.default_spin_count);
|
||||
defer nats.deinit();
|
||||
|
||||
const connection = try nats.Connection.connectTo(nats.default_server_url);
|
||||
defer connection.destroy();
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
const std = @import("std");
|
||||
|
||||
test {
|
||||
_ = @import("./nats.zig");
|
||||
_ = @import("./connection.zig");
|
||||
_ = @import("./message.zig");
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
const std = @import("std");
|
||||
const nats = @import("nats");
|
||||
|
||||
// const nats = @import("../src/nats.zig");
|
||||
// const message = @import("../src/message.zig");
|
||||
const nats = @import("nats");
|
||||
|
||||
test "message: create message" {
|
||||
const subject = "hello";
|
||||
@ -11,7 +9,7 @@ test "message: create message" {
|
||||
|
||||
// have to initialize the library so the reference counter can correctly destroy
|
||||
// objects, otherwise we segfault on trying to free the memory.
|
||||
try nats.init(-1);
|
||||
try nats.init(nats.default_spin_count);
|
||||
defer nats.deinit();
|
||||
|
||||
const message = try nats.Message.create(subject, reply, data);
|
||||
@ -28,7 +26,7 @@ test "message: create message" {
|
||||
}
|
||||
|
||||
test "message: get subject" {
|
||||
try nats.init(-1);
|
||||
try nats.init(nats.default_spin_count);
|
||||
defer nats.deinit();
|
||||
|
||||
const subject = "hello";
|
||||
@ -40,7 +38,7 @@ test "message: get subject" {
|
||||
}
|
||||
|
||||
test "message: get reply" {
|
||||
try nats.init(-1);
|
||||
try nats.init(nats.default_spin_count);
|
||||
defer nats.deinit();
|
||||
|
||||
const subject = "hello";
|
||||
|
96
tests/nats.zig
Normal file
96
tests/nats.zig
Normal file
@ -0,0 +1,96 @@
|
||||
const std = @import("std");
|
||||
|
||||
const nats = @import("nats");
|
||||
|
||||
test "version" {
|
||||
const version = nats.getVersion();
|
||||
const vernum = nats.getVersionNumber();
|
||||
|
||||
try std.testing.expectEqualStrings("3.6.1", version);
|
||||
try std.testing.expectEqual(@as(u32, 0x03_06_01), vernum);
|
||||
try std.testing.expect(nats.checkCompatibility());
|
||||
}
|
||||
|
||||
test "time" {
|
||||
const now = nats.now();
|
||||
const nownano = nats.nowInNanoseconds();
|
||||
|
||||
nats.sleep(1);
|
||||
|
||||
const later = nats.now();
|
||||
const laternano = nats.nowInNanoseconds();
|
||||
|
||||
try std.testing.expect(later >= now);
|
||||
try std.testing.expect(laternano >= nownano);
|
||||
}
|
||||
|
||||
test "init" {
|
||||
{
|
||||
try nats.init(nats.default_spin_count);
|
||||
defer nats.deinit();
|
||||
}
|
||||
|
||||
{
|
||||
// a completely random number
|
||||
try nats.init(900_142_069);
|
||||
nats.deinit();
|
||||
}
|
||||
|
||||
{
|
||||
try nats.init(0);
|
||||
try nats.deinitWait(1000);
|
||||
}
|
||||
}
|
||||
|
||||
test "misc" {
|
||||
{
|
||||
try nats.init(nats.default_spin_count);
|
||||
defer nats.deinit();
|
||||
|
||||
try nats.setMessageDeliveryPoolSize(500);
|
||||
}
|
||||
|
||||
{
|
||||
try nats.init(nats.default_spin_count);
|
||||
defer nats.deinit();
|
||||
|
||||
// just test that the function is wrapped properly
|
||||
nats.releaseThreadMemory();
|
||||
}
|
||||
|
||||
blk: {
|
||||
try nats.init(nats.default_spin_count);
|
||||
defer nats.deinit();
|
||||
|
||||
// this is a mess of a test that is designed to fail because actually we're
|
||||
// testing out the error reporting functions instead of signing. Nice bait
|
||||
// and switch.
|
||||
const signed = nats.sign("12345678", "12345678") catch {
|
||||
const err = nats.getLastError();
|
||||
std.debug.print("signing failed: {s}\n", .{err.desc});
|
||||
|
||||
var stackmem = [_]u8{0} ** 512;
|
||||
var stackbuf: []u8 = &stackmem;
|
||||
|
||||
nats.getLastErrorStack(&stackbuf) catch {
|
||||
std.debug.print("Actually, the error stack was too big\n", .{});
|
||||
break :blk;
|
||||
};
|
||||
|
||||
std.debug.print("stack: {s}\n", .{stackbuf});
|
||||
break :blk;
|
||||
};
|
||||
|
||||
std.heap.raw_c_allocator.free(signed);
|
||||
}
|
||||
}
|
||||
|
||||
test "inbox" {
|
||||
try nats.init(nats.default_spin_count);
|
||||
defer nats.deinit();
|
||||
|
||||
const inbox = try nats.createInbox();
|
||||
defer nats.destroyInbox(inbox);
|
||||
|
||||
std.debug.print("inbox: {s}\n", .{inbox});
|
||||
}
|
73
tests/util.zig
Normal file
73
tests/util.zig
Normal file
@ -0,0 +1,73 @@
|
||||
const std = @import("std");
|
||||
|
||||
const TestLaunchError = error{
|
||||
NoLaunchStringFound,
|
||||
};
|
||||
|
||||
pub const TestServer = struct {
|
||||
process: std.ChildProcess,
|
||||
|
||||
pub const LaunchOptions = struct {
|
||||
executable: []const u8 = "nats-server",
|
||||
port: u16 = 4222,
|
||||
auth: union(enum) {
|
||||
none: void,
|
||||
token: []const u8,
|
||||
password: struct { user: []const u8, pass: []const u8 },
|
||||
} = .none,
|
||||
allocator: std.mem.Allocator = std.testing.allocator,
|
||||
|
||||
fn argLen(self: LaunchOptions) usize {
|
||||
// executable, -a, 127.0.0.1, -p, 4222
|
||||
const base_len: usize = 5;
|
||||
return base_len + switch (self.auth) {
|
||||
.none => @as(usize, 0),
|
||||
.token => @as(usize, 2),
|
||||
.password => @as(usize, 4),
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub fn launch(options: LaunchOptions) !TestServer {
|
||||
// const allocator = std.testing.allocator;
|
||||
var portbuf = [_]u8{0} ** 5;
|
||||
const strport = try std.fmt.bufPrint(&portbuf, "{d}", .{options.port});
|
||||
|
||||
const argsbuf: [9][]const u8 = blk: {
|
||||
const executable: [1][]const u8 = .{options.executable};
|
||||
const listen: [2][]const u8 = .{ "-a", "127.0.0.1" };
|
||||
const port: [2][]const u8 = .{ "-p", strport };
|
||||
const auth: [4][]const u8 = switch (options.auth) {
|
||||
.none => .{""} ** 4,
|
||||
.token => |tok| .{ "--auth", tok, "", "" },
|
||||
.password => |auth| .{ "--user", auth.user, "--password", auth.pass },
|
||||
};
|
||||
|
||||
break :blk executable ++ listen ++ port ++ auth;
|
||||
};
|
||||
|
||||
const args = argsbuf[0..options.argLen()];
|
||||
|
||||
var child = std.ChildProcess.init(args, options.allocator);
|
||||
child.stdin_behavior = .Ignore;
|
||||
child.stdout_behavior = .Pipe;
|
||||
child.stderr_behavior = .Pipe;
|
||||
|
||||
try child.spawn();
|
||||
var poller = std.io.poll(options.allocator, enum { stderr }, .{ .stderr = child.stderr.? });
|
||||
defer poller.deinit();
|
||||
|
||||
while (try poller.poll()) {
|
||||
if (std.mem.indexOf(u8, poller.fifo(.stderr).buf, "[INF] Server is ready")) |_| {
|
||||
return .{ .process = child };
|
||||
}
|
||||
}
|
||||
|
||||
_ = try child.kill();
|
||||
return error.NoLaunchStringFound;
|
||||
}
|
||||
|
||||
pub fn stop(self: *TestServer) void {
|
||||
_ = self.process.kill() catch return;
|
||||
}
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user