163 lines
5.9 KiB
Zig
163 lines
5.9 KiB
Zig
const std = @import("std");
|
|
const util = @import("util");
|
|
const types = @import("../types.zig");
|
|
|
|
const Uuid = util.Uuid;
|
|
const DateTime = util.DateTime;
|
|
const RegistrationOptions = @import("../lib.zig").RegistrationOptions;
|
|
const UserResponse = @import("../lib.zig").UserResponse;
|
|
const Invite = @import("../lib.zig").Invite;
|
|
|
|
pub fn methods(comptime models: type) type {
|
|
return struct {
|
|
fn isInviteValid(invite: Invite) bool {
|
|
if (invite.max_uses != null and invite.times_used >= invite.max_uses.?) return false;
|
|
if (invite.expires_at != null and DateTime.now().isAfter(invite.expires_at.?)) return false;
|
|
return true;
|
|
}
|
|
pub fn register(self: anytype, username: []const u8, password: []const u8, opt: RegistrationOptions) !types.Actor {
|
|
const tx = try self.db.beginOrSavepoint();
|
|
const maybe_invite = if (opt.invite_code) |code|
|
|
try models.invites.getByCode(tx, code, self.context.community.id, self.allocator)
|
|
else
|
|
null;
|
|
defer if (maybe_invite) |inv| util.deepFree(self.allocator, inv);
|
|
|
|
if (maybe_invite) |invite| {
|
|
if (!Uuid.eql(invite.community_id, self.context.community.id)) return error.WrongCommunity;
|
|
if (!isInviteValid(invite)) return error.InvalidInvite;
|
|
}
|
|
|
|
const invite_kind = if (maybe_invite) |inv| inv.kind else .user;
|
|
|
|
if (self.context.community.kind == .admin) @panic("Unimplmented");
|
|
|
|
const user_id = try models.auth.register(
|
|
tx,
|
|
username,
|
|
password,
|
|
self.context.community.id,
|
|
.{
|
|
.invite_id = if (maybe_invite) |inv| @as(?Uuid, inv.id) else null,
|
|
.email = opt.email,
|
|
},
|
|
self.allocator,
|
|
);
|
|
|
|
switch (invite_kind) {
|
|
.user => {},
|
|
.system => @panic("System user invites unimplemented"),
|
|
.community_owner => {
|
|
try models.communities.transferOwnership(tx, self.context.community.id, user_id);
|
|
},
|
|
}
|
|
|
|
const user = models.actors.get(tx, user_id, self.allocator) catch |err| switch (err) {
|
|
error.NotFound => return error.Unexpected,
|
|
else => |e| return e,
|
|
};
|
|
errdefer util.deepFree(self.allocator, user);
|
|
|
|
try tx.commitOrRelease();
|
|
return user;
|
|
}
|
|
};
|
|
}
|
|
|
|
const TestDb = struct {
|
|
tx_level: usize = 0,
|
|
rolled_back: bool = false,
|
|
committed: bool = false,
|
|
fn beginOrSavepoint(self: *TestDb) !*TestDb {
|
|
self.tx_level += 1;
|
|
return self;
|
|
}
|
|
|
|
fn rollback(self: *TestDb) void {
|
|
self.rolled_back = true;
|
|
self.tx_level -= 1;
|
|
}
|
|
|
|
fn commitOrRelease(self: *TestDb) !void {
|
|
self.committed = true;
|
|
self.tx_level -= 1;
|
|
}
|
|
};
|
|
|
|
test "register" {
|
|
comptime var exp_code = "code";
|
|
comptime var exp_community = Uuid.parse("a210c035-c9e1-4361-82a2-aaeac8e40dc6") catch unreachable;
|
|
comptime var uid = Uuid.parse("6d951fcc-1c9f-497b-9c96-31dfb9873708") catch unreachable;
|
|
|
|
const MockSvc = struct {
|
|
const invites = struct {
|
|
fn getByCode(db: *TestDb, code: []const u8, community_id: Uuid, alloc: std.mem.Allocator) !Invite {
|
|
try std.testing.expectEqual(db.tx_level, 1);
|
|
try std.testing.expectEqualStrings(exp_code, code);
|
|
try std.testing.expectEqual(exp_community, community_id);
|
|
|
|
return try util.deepClone(alloc, Invite{
|
|
.id = Uuid.parse("eac18f43-4dcc-489f-9fb5-4c1633e7b4e0") catch unreachable,
|
|
|
|
.created_by = Uuid.parse("6d951fcc-1c9f-497b-9c96-31dfb9873708") catch unreachable,
|
|
.community_id = exp_community,
|
|
.name = "test invite",
|
|
.code = exp_code,
|
|
|
|
.kind = .user,
|
|
|
|
.created_at = DateTime.parse("2022-12-21T09:05:50Z") catch unreachable,
|
|
.times_used = 0,
|
|
|
|
.expires_at = null,
|
|
.max_uses = null,
|
|
});
|
|
}
|
|
};
|
|
const auth = struct {
|
|
fn register(
|
|
db: *TestDb,
|
|
username: []const u8,
|
|
password: []const u8,
|
|
community_id: Uuid,
|
|
_: @import("../services/auth.zig").RegistrationOptions,
|
|
_: std.mem.Allocator,
|
|
) anyerror!Uuid {
|
|
try std.testing.expectEqual(db.tx_level, 1);
|
|
try std.testing.expectEqualStrings("root", username);
|
|
try std.testing.expectEqualStrings("password", password);
|
|
try std.testing.expectEqual(exp_community, community_id);
|
|
|
|
return uid;
|
|
}
|
|
};
|
|
const actors = struct {
|
|
fn get(_: *TestDb, id: Uuid, alloc: std.mem.Allocator) anyerror!types.Actor {
|
|
try std.testing.expectEqual(uid, id);
|
|
return try util.deepClone(alloc, std.mem.zeroInit(types.Actor, .{
|
|
.id = id,
|
|
.username = "root",
|
|
.host = "example.com",
|
|
.community_id = exp_community,
|
|
}));
|
|
}
|
|
};
|
|
const communities = struct {
|
|
fn transferOwnership(_: *TestDb, _: Uuid, _: Uuid) anyerror!void {}
|
|
};
|
|
};
|
|
|
|
var db = TestDb{};
|
|
util.deepFree(std.testing.allocator, try methods(MockSvc).register(.{
|
|
.db = &db,
|
|
.allocator = std.testing.allocator,
|
|
.community = .{
|
|
.id = exp_community,
|
|
.kind = .local,
|
|
},
|
|
}, "root", "password", .{}));
|
|
try std.testing.expectEqual(false, db.rolled_back);
|
|
try std.testing.expectEqual(true, db.committed);
|
|
try std.testing.expectEqual(@as(usize, 0), db.tx_level);
|
|
}
|