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() !void { database = try pg.Pool.init(std.testing.allocator, .{ .connect = .{ .host = "localhost", .port = 5432, }, .auth = .{ .username = "zrm", .password = "zrm", .database = "zrm", }, .size = 1, }); } /// An example model with composite key. const CompositeModel = struct { firstcol: i32, secondcol: []const u8, label: ?[]const u8 = null, }; /// SQL table shape of the example model with composite key. const CompositeModelTable = struct { firstcol: i32, secondcol: []const u8, label: ?[]const u8, }; // Convert an SQL row to a model. fn modelFromSql(raw: CompositeModelTable) !CompositeModel { return .{ .firstcol = raw.firstcol, .secondcol = raw.secondcol, .label = raw.label, }; } /// Convert a model to an SQL row. fn modelToSql(model: CompositeModel) !CompositeModelTable { return .{ .firstcol = model.firstcol, .secondcol = model.secondcol, .label = model.label, }; } /// Declare the composite model repository. const CompositeModelRepository = zrm.Repository(CompositeModel, CompositeModelTable, .{ .table = "composite_models", // Insert shape used by default for inserts in the repository. .insertShape = struct { secondcol: []const u8, label: []const u8, }, .key = &[_][]const u8{"firstcol", "secondcol"}, .fromSql = &zrm.helpers.TableModel(CompositeModel, CompositeModelTable).copyTableToModel, .toSql = &zrm.helpers.TableModel(CompositeModel, CompositeModelTable).copyModelToTable, }); test "composite model create, save and find" { zrm.setDebug(true); try initDatabase(); defer database.deinit(); var poolConnector = zrm.database.PoolConnector{ .pool = database, }; // Initialize a test model. var newModel = CompositeModel{ .firstcol = 0, .secondcol = "identifier", .label = "test label", }; // Create the new model. var result = try CompositeModelRepository.create(std.testing.allocator, poolConnector.connector(), &newModel); defer result.deinit(); // Will clear some values in newModel. // Check that the model is correctly defined. try std.testing.expect(newModel.firstcol > 0); try std.testing.expectEqualStrings("identifier", newModel.secondcol); try std.testing.expectEqualStrings("test label", newModel.label.?); const postInsertFirstcol = newModel.firstcol; const postInsertSecondcol = newModel.secondcol; // Update the model. newModel.label = null; var result2 = try CompositeModelRepository.save(std.testing.allocator, poolConnector.connector(), &newModel); defer result2.deinit(); // Will clear some values in newModel. // Checking that the model has been updated (but only the right field). try std.testing.expectEqual(postInsertFirstcol, newModel.firstcol); try std.testing.expectEqualStrings(postInsertSecondcol, newModel.secondcol); try std.testing.expectEqual(null, newModel.label); // Do another insert with the same secondcol. var insertQuery = CompositeModelRepository.Insert.init(std.testing.allocator, poolConnector.connector()); defer insertQuery.deinit(); try insertQuery.values(.{ .secondcol = "identifier", .label = "test", }); insertQuery.returningAll(); var result3 = try insertQuery.insert(std.testing.allocator); defer result3.deinit(); // Checking that the other model has been inserted correctly. try std.testing.expect(result3.first().?.firstcol > newModel.firstcol); try std.testing.expectEqualStrings("identifier", result3.first().?.secondcol); try std.testing.expectEqualStrings("test", result3.first().?.label.?); // Try to find the created then saved model, to check that everything has been saved correctly. var result4 = try CompositeModelRepository.find(std.testing.allocator, poolConnector.connector(), .{ .firstcol = newModel.firstcol, .secondcol = newModel.secondcol, }); defer result4.deinit(); // Will clear some values in newModel. try std.testing.expectEqual(1, result4.models.len); try std.testing.expectEqualDeep(newModel, result4.first().?.*); // Try to find multiple models at once. var result5 = try CompositeModelRepository.find(std.testing.allocator, poolConnector.connector(), &[_]CompositeModelRepository.KeyType{ .{ .firstcol = newModel.firstcol, .secondcol = newModel.secondcol, }, .{ .firstcol = result3.first().?.firstcol, .secondcol = result3.first().?.secondcol, }, }); defer result5.deinit(); try std.testing.expectEqual(2, result5.models.len); try std.testing.expectEqual(newModel.firstcol, result5.models[0].firstcol); try std.testing.expectEqualStrings(newModel.secondcol, result5.models[0].secondcol); try std.testing.expectEqual(result3.first().?.firstcol, result5.models[1].firstcol); try std.testing.expectEqualStrings(result3.first().?.secondcol, result5.models[1].secondcol); }