╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ event.zig ────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
782 783 try std.testing.expectError(error.MergeConflict, evt.consume(repo_opts, io, allocator, &repo, .{ .kind = .head, .name = "haxy/meta" })); 784 } 785 786 test "user and repo" { 787 const io = std.testing.io; 788 const allocator = std.testing.allocator; 789 const temp_dir_name = "temp-event-user-and-repo"; 790 791 // create the temp dir 792 const cwd = std.Io.Dir.cwd(); 793 var temp_dir_or_err = cwd.openDir(io, temp_dir_name, .{}); 794 if (temp_dir_or_err) |*temp_dir| { 795 temp_dir.close(io); 796 try cwd.deleteTree(io, temp_dir_name); 797 } else |_| {} 798 var temp_dir = try cwd.createDirPathOpen(io, temp_dir_name, .{}); 799 defer cwd.deleteTree(io, temp_dir_name) catch {}; 800 defer temp_dir.close(io); 801 802 const cwd_path = try std.process.currentPathAlloc(io, allocator); 803 defer allocator.free(cwd_path); 804 805 const work_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name }); 806 defer allocator.free(work_path); 807 808 const repo_opts: rp.RepoOpts(.xit) = .{ .is_test = true }; 809 const Repo = rp.Repo(.xit, repo_opts); 810 var repo = try Repo.init(io, allocator, .{ .path = work_path }); 811 defer repo.deinit(io, allocator); 812 813 var arena = std.heap.ArenaAllocator.init(allocator); 814 defer arena.deinit(); 815 816 // 817 // define test events 818 // 819 820 var prng = std.Random.DefaultPrng.init(std.testing.random_seed); 821 const user_event_id = evt.randomId(prng.random()); 822 const repo_event_id = evt.randomId(prng.random()); 823 824 var first_password_hash_buf: [usr.password_hash_max_len]u8 = undefined; 825 const first_password_hash = try usr.hashPassword("correct horse battery staple", &first_password_hash_buf, io); 826 827 var second_password_hash_buf: [usr.password_hash_max_len]u8 = undefined; 828 const second_password_hash = try usr.hashPassword("Tr0ub4dor&3", &second_password_hash_buf, io); 829 830 const events_to_consume = [_]evt.Event{ 831 .{ 832 .id = std.fmt.bytesToHex(user_event_id, .lower), 833 .data = .{ 834 .user = .{ 835 .name = "Alice Example", 836 .email = "alice@example.test", 837 .password_hash = first_password_hash, 838 }, 839 }, 840 }, 841 // this event edits the previous one because it has the same id 842 .{ 843 .id = std.fmt.bytesToHex(user_event_id, .lower), 844 .data = .{ 845 .user = .{ 846 .name = "Alice Example", 847 .email = "alice@example.test", 848 .password_hash = second_password_hash, 849 }, 850 }, 851 }, 852 .{ 853 .id = std.fmt.bytesToHex(repo_event_id, .lower), 854 .data = .{ 855 .repo = .{ 856 .user_id = &user_event_id, 857 .name = "ziglings", 858 .enable_issue = true, 859 }, 860 }, 861 }, 862 }; 863 864 // 865 // insert users and repos as commits in the repo 866 // 867 868 { 869 var json: std.Io.Writer.Allocating = .init(std.testing.allocator); 870 defer json.deinit(); 871 872 for (events_to_consume) |event| { 873 json.clearRetainingCapacity(); 874 875 try std.json.Stringify.value(event, .{}, &json.writer); 876 877 // commit the event into a special branch 878 _ = try repo.commitAtRef(io, allocator, .{ .message = json.written() }, null, .{ .kind = .head, .name = "haxy/meta" }); 879 } 880 } 881 882 // 883 // consume events into the database 884 // 885 886 { 887 try evt.consume(repo_opts, io, allocator, &repo, .{ .kind = .head, .name = "haxy/meta" }); 888 889 const history = try Repo.DB.ArrayList(.read_only).init(repo.core.db.rootCursor().readOnly()); 890 891 // read the moment we just created 892 const moment_cursor = try history.getCursor(-1) orelse return error.NotFound; 893 const moment = try Repo.DB.HashMap(.read_only).init(moment_cursor); 894 895 // get the last object id 896 const last_object_id_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, "haxy-last-object-id")) orelse return error.NotFound; 897 var last_object_id: [hash.byteLen(repo_opts.hash)]u8 = undefined; 898 _ = try last_object_id_cursor.readBytes(&last_object_id); 899 900 const haxy_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, "haxy")) orelse return error.NotFound; 901 const haxy = try Repo.DB.ArrayList(.read_only).init(haxy_cursor); 902 903 try std.testing.expectEqual(1, try haxy.count()); 904 905 const haxy_moments_cursor = try haxy.getCursor(-1) orelse return error.NotFound; 906 const haxy_moments = try Repo.DB.HashMap(.read_only).init(haxy_moments_cursor); 907 908 const haxy_moment_cursor = try haxy_moments.getCursor(hash.bytesToInt(repo_opts.hash, &last_object_id)) orelse return error.NotFound; 909 const haxy_moment = try Repo.DB.HashMap(.read_only).init(haxy_moment_cursor); 910 911 // get the map of users 912 const event_id_to_user_cursor = try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, "event-id->user")) orelse return error.NotFound; 913 const event_id_to_user = try Repo.DB.HashMap(.read_only).init(event_id_to_user_cursor); 914 915 // get the user out of the map that was edited 916 const user_cursor = try event_id_to_user.getCursor(hash.hashInt(repo_opts.hash, &user_event_id)) orelse return error.NotFound; 917 const user_map = try Repo.DB.HashMap(.read_only).init(user_cursor); 918 const user = try evt.EventData.read(Repo.DB, repo_opts.hash, arena.allocator(), user_map, .user); 919 920 // the password was correctly edited 921 try std.testing.expectEqualStrings(events_to_consume[1].data.user.?.password_hash, user.user.password_hash); 922 923 // get the map of repos 924 const event_id_to_repo_cursor = try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, "event-id->repo")) orelse return error.NotFound; 925 const event_id_to_repo = try Repo.DB.HashMap(.read_only).init(event_id_to_repo_cursor); 926 927 // get the repo out of the map 928 const repo_cursor = try event_id_to_repo.getCursor(hash.hashInt(repo_opts.hash, &repo_event_id)) orelse return error.NotFound; 929 const repo_map = try Repo.DB.HashMap(.read_only).init(repo_cursor); 930 const repo_event = try evt.EventData.read(Repo.DB, repo_opts.hash, arena.allocator(), repo_map, .repo); 931 932 try std.testing.expectEqualSlices(u8, &user_event_id, repo_event.repo.user_id); 933 try std.testing.expectEqualStrings("ziglings", repo_event.repo.name); 934 935 // get the repos created by the user 936 const user_id_to_repos_cursor = try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, "user-id->repos")) orelse return error.NotFound; 937 const user_id_to_repos = try Repo.DB.HashMap(.read_only).init(user_id_to_repos_cursor); 938 939 const user_repos_cursor = try user_id_to_repos.getCursor(hash.hashInt(repo_opts.hash, &user_event_id)) orelse return error.NotFound; 940 const user_repos = try Repo.DB.CountedHashSet(.read_only).init(user_repos_cursor); 941 942 try std.testing.expectEqual(1, try user_repos.count()); 943 944 try std.testing.expect(null != try user_repos.getCursor(hash.hashInt(repo_opts.hash, &repo_event_id))); 945 } 946 947 // 948 // remove the repo 949 // 950 951 const events_to_consume2 = [_]evt.Event{ 952 .{ 953 .id = std.fmt.bytesToHex(repo_event_id, .lower), 954 .data = .{ .repo = null }, 955 }, 956 }; 957 958 { 959 var json: std.Io.Writer.Allocating = .init(std.testing.allocator); 960 defer json.deinit(); 961 962 for (events_to_consume2) |event| { 963 json.clearRetainingCapacity(); 964 965 try std.json.Stringify.value(event, .{}, &json.writer); 966 967 // commit the event into a special branch 968 _ = try repo.commitAtRef(io, allocator, .{ .message = json.written() }, null, .{ .kind = .head, .name = "haxy/meta" }); 969 } 970 } 971 972 // 973 // consume the removal into the database 974 // 975 976 { 977 try evt.consume(repo_opts, io, allocator, &repo, .{ .kind = .head, .name = "haxy/meta" }); 978 979 const history = try Repo.DB.ArrayList(.read_only).init(repo.core.db.rootCursor().readOnly()); 980 981 // read the moment we just created 982 const moment_cursor = try history.getCursor(-1) orelse return error.NotFound; 983 const moment = try Repo.DB.HashMap(.read_only).init(moment_cursor); 984 985 // get the last object id 986 const last_object_id_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, "haxy-last-object-id")) orelse return error.NotFound; 987 var last_object_id: [hash.byteLen(repo_opts.hash)]u8 = undefined; 988 _ = try last_object_id_cursor.readBytes(&last_object_id); 989 990 const haxy_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, "haxy")) orelse return error.NotFound; 991 const haxy = try Repo.DB.ArrayList(.read_only).init(haxy_cursor); 992 993 try std.testing.expectEqual(2, try haxy.count()); 994 995 const haxy_moments_cursor = try haxy.getCursor(-1) orelse return error.NotFound; 996 const haxy_moments = try Repo.DB.HashMap(.read_only).init(haxy_moments_cursor); 997 998 const haxy_moment_cursor = try haxy_moments.getCursor(hash.bytesToInt(repo_opts.hash, &last_object_id)) orelse return error.NotFound; 999 const haxy_moment = try Repo.DB.HashMap(.read_only).init(haxy_moment_cursor); 1000 1001 // get the map of repos 1002 const event_id_to_repo_cursor = try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, "event-id->repo")) orelse return error.NotFound; 1003 const event_id_to_repo = try Repo.DB.HashMap(.read_only).init(event_id_to_repo_cursor); 1004 1005 try std.testing.expect(null == try event_id_to_repo.getCursor(hash.hashInt(repo_opts.hash, &repo_event_id))); 1006 1007 // get the repos created by the user 1008 const user_id_to_repos_cursor = try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, "user-id->repos")) orelse return error.NotFound; 1009 const user_id_to_repos = try Repo.DB.HashMap(.read_only).init(user_id_to_repos_cursor); 1010 1011 const user_repos_cursor = try user_id_to_repos.getCursor(hash.hashInt(repo_opts.hash, &user_event_id)) orelse return error.NotFound; 1012 const user_repos = try Repo.DB.CountedHashSet(.read_only).init(user_repos_cursor); 1013 1014 try std.testing.expectEqual(0, try user_repos.count()); 1015 try std.testing.expect(null == try user_repos.getCursor(hash.hashInt(repo_opts.hash, &repo_event_id))); 1016 } 1017 1018 // 1019 // remove the user 1020 // 1021 1022 const events_to_consume3 = [_]evt.Event{ 1023 .{ 1024 .id = std.fmt.bytesToHex(user_event_id, .lower), 1025 .data = .{ .user = null }, 1026 }, 1027 }; 1028 1029 { 1030 var json: std.Io.Writer.Allocating = .init(std.testing.allocator); 1031 defer json.deinit(); 1032 1033 for (events_to_consume3) |event| { 1034 json.clearRetainingCapacity(); 1035 1036 try std.json.Stringify.value(event, .{}, &json.writer); 1037 1038 // commit the event into a special branch 1039 _ = try repo.commitAtRef(io, allocator, .{ .message = json.written() }, null, .{ .kind = .head, .name = "haxy/meta" }); 1040 } 1041 } 1042 1043 // 1044 // consume the user removal into the database 1045 // 1046 1047 { 1048 try evt.consume(repo_opts, io, allocator, &repo, .{ .kind = .head, .name = "haxy/meta" }); 1049 1050 const history = try Repo.DB.ArrayList(.read_only).init(repo.core.db.rootCursor().readOnly()); 1051 1052 // read the moment we just created 1053 const moment_cursor = try history.getCursor(-1) orelse return error.NotFound; 1054 const moment = try Repo.DB.HashMap(.read_only).init(moment_cursor); 1055 1056 // get the last object id 1057 const last_object_id_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, "haxy-last-object-id")) orelse return error.NotFound; 1058 var last_object_id: [hash.byteLen(repo_opts.hash)]u8 = undefined; 1059 _ = try last_object_id_cursor.readBytes(&last_object_id); 1060 1061 const haxy_cursor = try moment.getCursor(hash.hashInt(repo_opts.hash, "haxy")) orelse return error.NotFound; 1062 const haxy = try Repo.DB.ArrayList(.read_only).init(haxy_cursor); 1063 1064 try std.testing.expectEqual(3, try haxy.count()); 1065 1066 const haxy_moments_cursor = try haxy.getCursor(-1) orelse return error.NotFound; 1067 const haxy_moments = try Repo.DB.HashMap(.read_only).init(haxy_moments_cursor); 1068 1069 const haxy_moment_cursor = try haxy_moments.getCursor(hash.bytesToInt(repo_opts.hash, &last_object_id)) orelse return error.NotFound; 1070 const haxy_moment = try Repo.DB.HashMap(.read_only).init(haxy_moment_cursor); 1071 1072 // get the map of users 1073 const event_id_to_user_cursor = try haxy_moment.getCursor(hash.hashInt(repo_opts.hash, "event-id->user")) orelse return error.NotFound; 1074 const event_id_to_user = try Repo.DB.HashMap(.read_only).init(event_id_to_user_cursor); 1075 1076 try std.testing.expect(null == try event_id_to_user.getCursor(hash.hashInt(repo_opts.hash, &user_event_id))); 1077 } 1078 } 1079