Skip to content
zit Artifact [17cdf8ba0f]
Login

Artifact [17cdf8ba0f]

Artifact 17cdf8ba0f6ff06e1635bf10f50ad6933c7721940ea6a88dc0767aa11b8f3e08:


//! MirrorState container tests, on the repocache model: a validated HIT
//! round-trips exactly; a stale/corrupt/truncated/wrong-anything file is a
//! DISTINCT load error, never a partial load; the atomic publish never
//! leaves a torn file authoritative.

const std = @import("std");
const state = @import("git_export_state.zig");
const logstore = @import("../logstore.zig");

const testing = std.testing;

const key_a: logstore.LogStore.LogKey = .{
    .end = 4096,
    .frame_count = 7,
    .last_frame_offset = 3968,
    .last_header_crc = 0xDEAD_BEEF,
};

const hash_a = "a" ** 64;
const hash_b = "b" ** 40; // SHA-1-sized names round-trip too
const oid_a = "0123456789abcdef0123456789abcdef01234567";

fn sampleRecords() [2]state.Record {
    return .{
        .{ .hash = hash_a, .mark = 1, .oid = oid_a, .kind = .commit },
        .{ .hash = hash_b, .mark = 2, .oid = oid_a, .kind = .tag },
    };
}

fn sampleRefs() [1]state.Ref {
    return .{.{ .name = "refs/heads/trunk", .oid = oid_a }};
}

test "export mirror state: a validated hit round-trips key, flag, records, refs, and the hash index" {
    const io = testing.io;
    const gpa = testing.allocator;
    var tmp = testing.tmpDir(.{});
    defer tmp.cleanup();

    const records = sampleRecords();
    const refs = sampleRefs();
    try testing.expect(state.store(gpa, io, tmp.dir, key_a, true, &records, &refs));

    var loaded = try state.load(gpa, io, tmp.dir);
    defer loaded.deinit();
    try testing.expectEqual(key_a.end, loaded.source_key.end);
    try testing.expectEqual(key_a.frame_count, loaded.source_key.frame_count);
    try testing.expectEqual(key_a.last_frame_offset, loaded.source_key.last_frame_offset);
    try testing.expectEqual(key_a.last_header_crc, loaded.source_key.last_header_crc);
    try testing.expect(loaded.provisional_fixture);
    try testing.expectEqual(@as(usize, 2), loaded.records.len);
    try testing.expectEqualStrings(hash_a, loaded.records[0].hash);
    try testing.expectEqual(@as(u64, 1), loaded.records[0].mark);
    try testing.expectEqualStrings(oid_a, loaded.records[0].oid);
    try testing.expectEqual(state.RecordKind.commit, loaded.records[0].kind);
    try testing.expectEqual(state.RecordKind.tag, loaded.records[1].kind);
    try testing.expectEqual(@as(usize, 1), loaded.refs.len);
    try testing.expectEqualStrings("refs/heads/trunk", loaded.refs[0].name);

    const hit = loaded.containsHash(hash_b) orelse return error.TestUnexpectedResult;
    try testing.expectEqual(@as(u64, 2), hit.mark);
    try testing.expect(loaded.containsHash("c" ** 64) == null);
}

test "export mirror state: absence is Absent — the ordinary first run, distinct from damage" {
    const io = testing.io;
    var tmp = testing.tmpDir(.{});
    defer tmp.cleanup();
    try testing.expectError(error.Absent, state.load(testing.allocator, io, tmp.dir));
}

fn storedBytes(gpa: std.mem.Allocator, io: std.Io, dir: std.Io.Dir) ![]u8 {
    return dir.readFileAlloc(io, state.state_filename, gpa, .limited(1 << 20));
}

fn rewrite(io: std.Io, dir: std.Io.Dir, bytes: []const u8) !void {
    var f = try dir.createFile(io, state.state_filename, .{ .truncate = true });
    defer f.close(io);
    try f.writePositionalAll(io, bytes, 0);
}

test "export mirror state: every framing violation is a distinct error, never a partial load" {
    const io = testing.io;
    const gpa = testing.allocator;
    var tmp = testing.tmpDir(.{});
    defer tmp.cleanup();

    const records = sampleRecords();
    const refs = sampleRefs();
    try testing.expect(state.store(gpa, io, tmp.dir, key_a, false, &records, &refs));
    const good = try storedBytes(gpa, io, tmp.dir);
    defer gpa.free(good);

    // Wrong magic.
    {
        const bad = try gpa.dupe(u8, good);
        defer gpa.free(bad);
        bad[0] = 'X';
        try rewrite(io, tmp.dir, bad);
        try testing.expectError(error.WrongMagic, state.load(gpa, io, tmp.dir));
    }
    // Wrong version.
    {
        const bad = try gpa.dupe(u8, good);
        defer gpa.free(bad);
        bad[8] = 0xEE;
        try rewrite(io, tmp.dir, bad);
        try testing.expectError(error.WrongVersion, state.load(gpa, io, tmp.dir));
    }
    // A flipped byte ANYWHERE past the version is a checksum mismatch (the
    // magic/version prefix reports its own named error first).
    {
        var offset: usize = 10;
        while (offset < good.len) : (offset += 7) {
            const bad = try gpa.dupe(u8, good);
            defer gpa.free(bad);
            bad[offset] ^= 0xFF;
            try rewrite(io, tmp.dir, bad);
            const result = state.load(gpa, io, tmp.dir);
            try testing.expectError(error.ChecksumMismatch, result);
        }
    }
    // Truncation at every 8-byte boundary: named truncation-class errors,
    // never success (below the 10-byte prefix the magic/version reads fail).
    {
        var cut: usize = 0;
        while (cut < good.len) : (cut += 8) {
            try rewrite(io, tmp.dir, good[0..cut]);
            const result = state.load(gpa, io, tmp.dir);
            if (result) |_| return error.TestUnexpectedResult else |err| switch (err) {
                error.Truncated, error.ChecksumMismatch, error.WrongMagic, error.WrongVersion => {},
                else => return err,
            }
        }
    }
    // A record count lying about the body: crc-honest rewrite still refuses.
    {
        const bad = try gpa.dupe(u8, good);
        defer gpa.free(bad);
        const count_at = 8 + 2 + 8 + 8 + 8 + 4 + 1;
        std.mem.writeInt(u64, bad[count_at..][0..8], 10_000, .little);
        var crc = @import("../crc32.zig").Crc32.init();
        crc.update(bad[0 .. bad.len - 4]);
        std.mem.writeInt(u32, bad[bad.len - 4 ..][0..4], crc.final(), .little);
        try rewrite(io, tmp.dir, bad);
        try testing.expectError(error.LyingLength, state.load(gpa, io, tmp.dir));
    }

    // Control: the untouched bytes still load.
    try rewrite(io, tmp.dir, good);
    var loaded = try state.load(gpa, io, tmp.dir);
    loaded.deinit();
}

test "export mirror state: a stray temp or a replaced-and-torn write never unseats a good state" {
    const io = testing.io;
    const gpa = testing.allocator;
    var tmp = testing.tmpDir(.{});
    defer tmp.cleanup();

    const records = sampleRecords();
    const refs = sampleRefs();
    try testing.expect(state.store(gpa, io, tmp.dir, key_a, false, &records, &refs));

    // Crash shape 1: a temp file left behind beside the member (the moment
    // between temp write and rename) — the published state is untouched.
    {
        var f = try tmp.dir.createFile(io, state.state_dirname ++ "/state.tmp-crash", .{});
        try f.writePositionalAll(io, "torn temp garbage", 0);
        f.close(io);
        var loaded = try state.load(gpa, io, tmp.dir);
        loaded.deinit();
    }
    // Crash shape 2: a successor store OVER the member is atomic — after a
    // successful second store the new content is whole (no torn middle
    // state observable through the member name).
    {
        const more = [_]state.Record{.{ .hash = "d" ** 64, .mark = 9, .oid = oid_a, .kind = .commit }};
        try testing.expect(state.store(gpa, io, tmp.dir, key_a, false, &more, &refs));
        var loaded = try state.load(gpa, io, tmp.dir);
        defer loaded.deinit();
        try testing.expectEqual(@as(usize, 1), loaded.records.len);
        try testing.expectEqual(@as(u64, 9), loaded.records[0].mark);
    }
}

test "export mirror state: prefix extension admits growth, refuses shrink and a swapped tail" {
    const grown: logstore.LogStore.LogKey = .{ .end = 8192, .frame_count = 11, .last_frame_offset = 8000, .last_header_crc = 1 };
    try testing.expect(state.MirrorState.isPrefixExtensionOf(key_a, grown));
    // Identical log: trivially a prefix.
    try testing.expect(state.MirrorState.isPrefixExtensionOf(key_a, key_a));
    // Shrunk log: refused.
    const shrunk: logstore.LogStore.LogKey = .{ .end = 100, .frame_count = 1, .last_frame_offset = 0, .last_header_crc = 1 };
    try testing.expect(!state.MirrorState.isPrefixExtensionOf(key_a, shrunk));
    // Same length, different tail identity: a generation swap, refused.
    var swapped = key_a;
    swapped.last_header_crc = 0x1234_5678;
    try testing.expect(!state.MirrorState.isPrefixExtensionOf(key_a, swapped));
}