fetch_ml/cli/tests/rsync_embedded_test.zig
Jeremie Fraeys 8e3fa94322
feat(cli): enhance Zig CLI with new commands and improved networking
- Add new commands: annotate, narrative, requeue
- Refactor WebSocket client into modular components (net/ws/)
- Add rsync embedded binary support
- Improve error handling and response packet processing
- Update build.zig and completions
2026-02-12 12:05:10 -05:00

121 lines
3.8 KiB
Zig

const std = @import("std");
const testing = std.testing;
const c = @cImport({
@cInclude("stdlib.h");
});
const src = @import("src");
const utils = src.utils;
fn isScript(data: []const u8) bool {
return data.len >= 2 and data[0] == '#' and data[1] == '!';
}
test "embedded rsync binary creation" {
const allocator = testing.allocator;
// Ensure the override doesn't influence this test.
_ = c.unsetenv("ML_RSYNC_PATH");
// Force embedded fallback by clearing PATH for this process so system rsync isn't found.
const old_path_z = std.posix.getenv("PATH");
if (c.setenv("PATH", "", 1) != 0) return error.Unexpected;
defer {
if (old_path_z) |z| {
_ = c.setenv("PATH", z, 1);
} else {
_ = c.unsetenv("PATH");
}
}
var embedded_rsync = utils.rsync_embedded.EmbeddedRsync{ .allocator = allocator };
const rsync_path = try embedded_rsync.extractRsyncBinary();
defer allocator.free(rsync_path);
try testing.expect(rsync_path.len > 0);
// File exists and is executable
const file = try std.fs.openFileAbsolute(rsync_path, .{});
defer file.close();
const stat = try file.stat();
try testing.expect(stat.mode & 0o111 != 0);
// Contents match embedded payload
const data = try file.readToEndAlloc(allocator, 1024 * 1024 * 4);
defer allocator.free(data);
var digest: [32]u8 = undefined;
std.crypto.hash.sha2.Sha256.hash(data, &digest, .{});
const embedded_digest = utils.rsync_embedded_binary.rsyncBinarySha256();
try testing.expect(std.mem.eql(u8, &digest, &embedded_digest));
// Sanity: if we're not using the release binary, it should be the placeholder script.
if (!utils.rsync_embedded_binary.USING_RELEASE_BINARY) {
try testing.expect(isScript(data));
}
}
test "embedded rsync honors ML_RSYNC_PATH override" {
const allocator = testing.allocator;
// Use a known executable. We don't execute it; we only verify the override is returned.
const override_path = "/bin/sh";
try testing.expect(std.fs.openFileAbsolute(override_path, .{}) != error.FileNotFound);
if (c.setenv("ML_RSYNC_PATH", override_path, 1) != 0) return error.Unexpected;
defer _ = c.unsetenv("ML_RSYNC_PATH");
var embedded_rsync = utils.rsync_embedded.EmbeddedRsync{ .allocator = allocator };
const rsync_path = try embedded_rsync.extractRsyncBinary();
defer allocator.free(rsync_path);
try testing.expectEqualStrings(override_path, rsync_path);
}
test "embedded rsync prefers system rsync when available" {
const allocator = testing.allocator;
_ = c.unsetenv("ML_RSYNC_PATH");
// Find rsync on PATH (simple scan). If not present, skip.
const path_z = std.posix.getenv("PATH") orelse return;
const path = std.mem.sliceTo(path_z, 0);
var sys_rsync: ?[]u8 = null;
defer if (sys_rsync) |p| allocator.free(p);
var it = std.mem.splitScalar(u8, path, ':');
while (it.next()) |dir| {
if (dir.len == 0) continue;
const candidate = std.fmt.allocPrint(allocator, "{s}/{s}", .{ dir, "rsync" }) catch return;
errdefer allocator.free(candidate);
const file = std.fs.openFileAbsolute(candidate, .{}) catch {
allocator.free(candidate);
continue;
};
defer file.close();
const st = file.stat() catch {
allocator.free(candidate);
continue;
};
if (st.mode & 0o111 == 0) {
allocator.free(candidate);
continue;
}
sys_rsync = candidate;
break;
}
if (sys_rsync == null) return;
var embedded_rsync = utils.rsync_embedded.EmbeddedRsync{ .allocator = allocator };
const rsync_path = try embedded_rsync.extractRsyncBinary();
defer allocator.free(rsync_path);
try testing.expectEqualStrings(sys_rsync.?, rsync_path);
}