Add a new full example model with all relations types.

+ Add a new example model.
+ Test all relations types on example model.
This commit is contained in:
Madeorsk 2024-11-26 12:55:46 +01:00
parent 854c66e78b
commit 3501c48eaf
Signed by: Madeorsk
GPG key ID: 677E51CA765BB79F
3 changed files with 526 additions and 0 deletions

462
tests/example.zig Normal file
View file

@ -0,0 +1,462 @@
const std = @import("std");
const pg = @import("pg");
const zrm = @import("zrm");
/// PostgreSQL database connection.
var database: *pg.Pool = undefined;
/// Initialize database connection.
fn initDatabase(allocator: std.mem.Allocator) !void {
database = try pg.Pool.init(allocator, .{
.connect = .{
.host = "localhost",
.port = 5432,
},
.auth = .{
.username = "zrm",
.password = "zrm",
.database = "zrm",
},
.size = 5,
});
}
pub const Media = struct {
pub const Table = struct {
id: i32,
filename: []const u8,
pub const Insert = struct {
filename: []const u8,
};
};
id: i32,
filename: []const u8,
};
pub const MediaRepository = zrm.Repository(Media, Media.Table, .{
.table = "example_medias",
.insertShape = Media.Table.Insert,
.key = &[_][]const u8{"id"},
.fromSql = zrm.helpers.TableModel(Media, Media.Table).copyTableToModel,
.toSql = zrm.helpers.TableModel(Media, Media.Table).copyModelToTable,
});
pub const User = struct {
pub const Table = struct {
id: i32,
name: []const u8,
picture_id: ?i32,
pub const Insert = struct {
name: []const u8,
picture_id: ?i32,
};
};
id: i32,
name: []const u8,
picture_id: ?i32,
picture: ?Media = null,
info: ?UserInfo = null,
messages: ?[]Message = null,
medias: ?[]Media = null,
};
pub const UserRepository = zrm.Repository(User, User.Table, .{
.table = "example_users",
.insertShape = User.Table.Insert,
.key = &[_][]const u8{"id"},
.fromSql = zrm.helpers.TableModel(User, User.Table).copyTableToModel,
.toSql = zrm.helpers.TableModel(User, User.Table).copyModelToTable,
});
pub const UserRelations = UserRepository.relations.define(.{
.picture = UserRepository.relations.one(MediaRepository, .{
.direct = .{
.foreignKey = "picture_id",
}
}),
.info = UserRepository.relations.one(UserInfoRepository, .{
.reverse = .{},
}),
.messages = UserRepository.relations.many(MessageRepository, .{
.direct = .{
.foreignKey = "user_id",
},
}),
//TODO double through to get medias?
});
pub const UserInfo = struct {
pub const Table = struct {
user_id: i32,
birthdate: i64,
pub const Insert = struct {
user_id: i32,
birthdate: i64,
};
};
user_id: i32,
birthdate: i64,
//TODO is there a way to solve the "struct 'example.User' depends on itself" error when adding this relation?
// user: ?User = null,
};
pub const UserInfoRepository = zrm.Repository(UserInfo, UserInfo.Table, .{
.table = "example_users_info",
.insertShape = UserInfo.Table.Insert,
.key = &[_][]const u8{"user_id"},
.fromSql = zrm.helpers.TableModel(UserInfo, UserInfo.Table).copyTableToModel,
.toSql = zrm.helpers.TableModel(UserInfo, UserInfo.Table).copyModelToTable,
});
pub const UserInfoRelations = UserInfoRepository.relations.define(.{
// .user = UserInfoRepository.relations.one(UserRepository, .{
// .direct = .{
// .foreignKey = "user_id",
// },
// }),
});
pub const Message = struct {
pub const Table = struct {
id: i32,
text: []const u8,
user_id: i32,
pub const Insert = struct {
text: []const u8,
user_id: i32,
};
};
id: i32,
text: []const u8,
user_id: i32,
user: ?User = null,
user_picture: ?Media = null,
medias: ?[]Media = null,
};
pub const MessageRepository = zrm.Repository(Message, Message.Table, .{
.table = "example_messages",
.insertShape = Message.Table.Insert,
.key = &[_][]const u8{"id"},
.fromSql = zrm.helpers.TableModel(Message, Message.Table).copyTableToModel,
.toSql = zrm.helpers.TableModel(Message, Message.Table).copyModelToTable,
});
pub const MessageRelations = MessageRepository.relations.define(.{
.user = MessageRepository.relations.one(UserRepository, .{
.direct = .{
.foreignKey = "user_id",
}
}),
.user_picture = MessageRepository.relations.one(MediaRepository, .{
.through = .{
.table = "example_users",
.foreignKey = "user_id",
.joinForeignKey = "id",
.joinModelKey = "picture_id",
},
}),
.medias = MessageRepository.relations.many(MediaRepository, .{
.through = .{
.table = "example_messages_medias",
.joinModelKey = "message_id",
.joinForeignKey = "media_id",
},
}),
});
test "user picture media" {
zrm.setDebug(true);
try initDatabase(std.testing.allocator);
defer database.deinit();
var poolConnector = zrm.database.PoolConnector{
.pool = database,
};
var firstQuery = UserRepository.QueryWith(
// Retrieve picture of users.
&[_]zrm.relations.Relation{UserRelations.picture}
).init(std.testing.allocator, poolConnector.connector(), .{});
try firstQuery.whereKey(1);
defer firstQuery.deinit();
var firstResult = try firstQuery.get(std.testing.allocator);
defer firstResult.deinit();
try std.testing.expectEqual(1, firstResult.models.len);
try std.testing.expectEqual(1, firstResult.models[0].picture_id);
try std.testing.expectEqual(1, firstResult.models[0].picture.?.id);
try std.testing.expectEqualStrings("profile.jpg", firstResult.models[0].picture.?.filename);
var secondQuery = UserRepository.QueryWith(
// Retrieve picture of users.
&[_]zrm.relations.Relation{UserRelations.picture}
).init(std.testing.allocator, poolConnector.connector(), .{});
try secondQuery.whereKey(3);
defer secondQuery.deinit();
var secondResult = try secondQuery.get(std.testing.allocator);
defer secondResult.deinit();
try std.testing.expectEqual(1, secondResult.models.len);
try std.testing.expectEqual(2, secondResult.models[0].picture_id);
try std.testing.expectEqual(2, secondResult.models[0].picture.?.id);
try std.testing.expectEqualStrings("profile.png", secondResult.models[0].picture.?.filename);
var thirdQuery = UserRepository.QueryWith(
// Retrieve picture of users.
&[_]zrm.relations.Relation{UserRelations.picture}
).init(std.testing.allocator, poolConnector.connector(), .{});
try thirdQuery.whereKey(5);
defer thirdQuery.deinit();
var thirdResult = try thirdQuery.get(std.testing.allocator);
defer thirdResult.deinit();
try std.testing.expectEqual(1, thirdResult.models.len);
try std.testing.expectEqual(null, thirdResult.models[0].picture_id);
try std.testing.expectEqual(null, thirdResult.models[0].picture);
var fourthQuery = UserRepository.QueryWith(
// Retrieve picture of users.
&[_]zrm.relations.Relation{UserRelations.picture}
).init(std.testing.allocator, poolConnector.connector(), .{});
try fourthQuery.whereKey(19);
defer fourthQuery.deinit();
var fourthResult = try fourthQuery.get(std.testing.allocator);
defer fourthResult.deinit();
try std.testing.expectEqual(0, fourthResult.models.len);
}
test "user has info" {
zrm.setDebug(true);
try initDatabase(std.testing.allocator);
defer database.deinit();
var poolConnector = zrm.database.PoolConnector{
.pool = database,
};
var firstQuery = UserRepository.QueryWith(
// Retrieve info of users.
&[_]zrm.relations.Relation{UserRelations.info}
).init(std.testing.allocator, poolConnector.connector(), .{});
try firstQuery.whereKey(2);
defer firstQuery.deinit();
var firstResult = try firstQuery.get(std.testing.allocator);
defer firstResult.deinit();
try std.testing.expectEqual(1, firstResult.models.len);
try std.testing.expect(firstResult.models[0].info != null);
try std.testing.expectEqual(2, firstResult.models[0].info.?.user_id);
try std.testing.expectEqual(876348000000000, firstResult.models[0].info.?.birthdate);
var secondQuery = UserRepository.QueryWith(
// Retrieve info of users.
&[_]zrm.relations.Relation{UserRelations.info}
).init(std.testing.allocator, poolConnector.connector(), .{});
try secondQuery.whereKey(1);
defer secondQuery.deinit();
var secondResult = try secondQuery.get(std.testing.allocator);
defer secondResult.deinit();
try std.testing.expectEqual(1, secondResult.models.len);
try std.testing.expect(secondResult.models[0].info == null);
}
test "user has many messages" {
zrm.setDebug(true);
try initDatabase(std.testing.allocator);
defer database.deinit();
var poolConnector = zrm.database.PoolConnector{
.pool = database,
};
var firstQuery = UserRepository.QueryWith(
// Retrieve messages of users.
&[_]zrm.relations.Relation{UserRelations.messages}
).init(std.testing.allocator, poolConnector.connector(), .{});
try firstQuery.whereKey(1);
defer firstQuery.deinit();
var firstResult = try firstQuery.get(std.testing.allocator);
defer firstResult.deinit();
try std.testing.expectEqual(1, firstResult.models.len);
try std.testing.expect(firstResult.models[0].messages != null);
try std.testing.expectEqual(3, firstResult.models[0].messages.?.len);
for (firstResult.models[0].messages.?) |message| {
if (message.id == 2) {
try std.testing.expectEqualStrings("I want to test something.", message.text);
} else if (message.id == 3) {
try std.testing.expectEqualStrings("Lorem ipsum dolor sit amet", message.text);
} else if (message.id == 6) {
try std.testing.expectEqualStrings("foo bar baz", message.text);
} else {
try std.testing.expect(false);
}
}
var secondQuery = UserRepository.QueryWith(
// Retrieve messages of users.
&[_]zrm.relations.Relation{UserRelations.messages}
).init(std.testing.allocator, poolConnector.connector(), .{});
try secondQuery.whereKey(5);
defer secondQuery.deinit();
var secondResult = try secondQuery.get(std.testing.allocator);
defer secondResult.deinit();
try std.testing.expectEqual(1, secondResult.models.len);
try std.testing.expect(secondResult.models[0].messages != null);
try std.testing.expectEqual(0, secondResult.models[0].messages.?.len);
}
test "message has many medias through pivot table" {
zrm.setDebug(true);
try initDatabase(std.testing.allocator);
defer database.deinit();
var poolConnector = zrm.database.PoolConnector{
.pool = database,
};
var firstQuery = MessageRepository.QueryWith(
// Retrieve medias of messages.
&[_]zrm.relations.Relation{MessageRelations.medias}
).init(std.testing.allocator, poolConnector.connector(), .{});
try firstQuery.whereKey(1);
defer firstQuery.deinit();
var firstResult = try firstQuery.get(std.testing.allocator);
defer firstResult.deinit();
try std.testing.expectEqual(1, firstResult.models.len);
try std.testing.expect(firstResult.models[0].medias != null);
try std.testing.expectEqual(1, firstResult.models[0].medias.?.len);
try std.testing.expectEqualStrings("attachment.png", firstResult.models[0].medias.?[0].filename);
var secondQuery = MessageRepository.QueryWith(
// Retrieve medias of messages.
&[_]zrm.relations.Relation{MessageRelations.medias}
).init(std.testing.allocator, poolConnector.connector(), .{});
try secondQuery.whereKey(6);
defer secondQuery.deinit();
var secondResult = try secondQuery.get(std.testing.allocator);
defer secondResult.deinit();
try std.testing.expectEqual(1, secondResult.models.len);
try std.testing.expect(secondResult.models[0].medias != null);
try std.testing.expectEqual(2, secondResult.models[0].medias.?.len);
for (secondResult.models[0].medias.?) |media| {
if (media.id == 3) {
try std.testing.expectEqualStrings("attachment.png", media.filename);
} else if (media.id == 5) {
try std.testing.expectEqualStrings("music.opus", media.filename);
} else {
try std.testing.expect(false);
}
}
var thirdQuery = MessageRepository.QueryWith(
// Retrieve medias of messages.
&[_]zrm.relations.Relation{MessageRelations.medias}
).init(std.testing.allocator, poolConnector.connector(), .{});
try thirdQuery.whereKey(4);
defer thirdQuery.deinit();
var thirdResult = try thirdQuery.get(std.testing.allocator);
defer thirdResult.deinit();
try std.testing.expectEqual(1, thirdResult.models.len);
try std.testing.expect(thirdResult.models[0].medias != null);
try std.testing.expectEqualStrings("Je pense donc je suis", thirdResult.models[0].text);
try std.testing.expectEqual(0, thirdResult.models[0].medias.?.len);
}
test "message has one user picture URL through users table" {
zrm.setDebug(true);
try initDatabase(std.testing.allocator);
defer database.deinit();
var poolConnector = zrm.database.PoolConnector{
.pool = database,
};
var firstQuery = MessageRepository.QueryWith(
// Retrieve user pictures of messages.
&[_]zrm.relations.Relation{MessageRelations.user_picture}
).init(std.testing.allocator, poolConnector.connector(), .{});
try firstQuery.whereKey(1);
defer firstQuery.deinit();
var firstResult = try firstQuery.get(std.testing.allocator);
defer firstResult.deinit();
try std.testing.expectEqual(1, firstResult.models.len);
try std.testing.expect(firstResult.models[0].user_picture != null);
try std.testing.expectEqualStrings("profile.jpg", firstResult.models[0].user_picture.?.filename);
var secondQuery = MessageRepository.QueryWith(
// Retrieve user pictures of messages.
&[_]zrm.relations.Relation{MessageRelations.user_picture}
).init(std.testing.allocator, poolConnector.connector(), .{});
try secondQuery.whereKey(4);
defer secondQuery.deinit();
var secondResult = try secondQuery.get(std.testing.allocator);
defer secondResult.deinit();
try std.testing.expectEqual(1, secondResult.models.len);
try std.testing.expect(secondResult.models[0].user_picture == null);
}
//TODO try to load all one relations types in another query (with buildQuery).

View file

@ -31,3 +31,66 @@ CREATE TABLE composite_models (
label VARCHAR NULL,
PRIMARY KEY (firstcol, secondcol)
);
-- Create example models.
CREATE TABLE example_medias (
id SERIAL PRIMARY KEY,
filename VARCHAR NOT NULL
);
CREATE TABLE example_users (
id SERIAL PRIMARY KEY,
name VARCHAR NOT NULL,
picture_id INT
);
CREATE TABLE example_users_info (
user_id INT PRIMARY KEY,
birthdate TIMESTAMP WITH TIME ZONE NOT NULL
);
CREATE TABLE example_messages (
id SERIAL PRIMARY KEY,
text TEXT NOT NULL,
user_id INT NOT NULL
);
CREATE TABLE example_messages_medias (
message_id INT NOT NULL,
media_id INT NOT NULL,
PRIMARY KEY (message_id, media_id)
);
-- Fill example models.
INSERT INTO example_medias (filename) VALUES ('profile.jpg'), ('profile.png'), ('attachment.png'), ('video.mp4'), ('music.opus');
INSERT INTO example_users (name, picture_id) VALUES
('test', 1),
('madeorsk', 1),
('foo', 2),
('bar', NULL),
('baz', NULL);
INSERT INTO example_users_info (user_id, birthdate) VALUES
(2, '1997-10-09');
INSERT INTO example_messages (text, user_id) VALUES
('this is a test', 2),
('I want to test something.', 1),
('Lorem ipsum dolor sit amet', 1),
('Je pense donc je suis', 4),
('The quick brown fox jumps over the lazy dog', 3),
('foo bar baz', 1),
('How are you?', 2),
('Fine!', 3);
INSERT INTO example_messages_medias (message_id, media_id) VALUES
(1, 3),
(2, 4),
(6, 3),
(6, 5),
(8, 2);

View file

@ -6,4 +6,5 @@ comptime {
_ = @import("composite.zig");
_ = @import("sessions.zig");
_ = @import("relations.zig");
_ = @import("example.zig");
}