604 lines
26 KiB
Zig
604 lines
26 KiB
Zig
const std = @import("std");
|
|
const expect = std.testing.expect;
|
|
const heap = std.heap;
|
|
const hash_map = std.hash_map;
|
|
const mem = std.mem;
|
|
const Thread = std.Thread;
|
|
const Mutex = Thread.Mutex;
|
|
const Allocator = mem.Allocator;
|
|
const testing = std.testing;
|
|
|
|
const DateTime = @import("things").DateTime;
|
|
|
|
const SubscriberFunc = fn (payment: *Payment) void;
|
|
|
|
pub const PaymentIntegrationStatus = enum { processing, processed, not_integrated };
|
|
|
|
pub const Payment = struct {
|
|
id: [36]u8,
|
|
amount: f64,
|
|
requested_at: DateTime,
|
|
integration_status: PaymentIntegrationStatus = .processing,
|
|
processed_by: u64 = undefined,
|
|
|
|
pub fn getIntegrationStatus(self: *const Payment) []const u8 {
|
|
return switch (self.integration_status) {
|
|
.processing => "processing",
|
|
.processed => "processed",
|
|
.not_integrated => "not integrated"
|
|
};
|
|
}
|
|
};
|
|
|
|
pub const PaymentsSummary = struct {
|
|
total_payments_processed: usize,
|
|
total_value: f64,
|
|
};
|
|
|
|
pub const PaymentsIntegrationSummary = struct {
|
|
default_total_payments_processed: usize,
|
|
default_total_value: f64,
|
|
fallback_total_payments_processed: usize,
|
|
fallback_total_value: f64,
|
|
};
|
|
|
|
const CONTAINER_INCREASE_RATE = 32;
|
|
const MAX_CONTAINER_INCREASE = 32 * CONTAINER_INCREASE_RATE;
|
|
|
|
inline fn calculateMaxIndexes(tee: usize) usize {
|
|
return ((tee / CONTAINER_INCREASE_RATE) + 1);
|
|
}
|
|
|
|
pub inline fn calculateNecessaryMemory(tee: usize) usize {
|
|
return (@sizeOf(Payment) * tee) + (@sizeOf(IndexContainerUnsafe) * calculateMaxIndexes(tee)) + (calculateMaxIndexes(tee) * MAX_CONTAINER_INCREASE);
|
|
}
|
|
|
|
inline fn newContainerSize(c: usize) usize {
|
|
return c + CONTAINER_INCREASE_RATE;
|
|
}
|
|
|
|
const IndexContainerUnsafe = struct {
|
|
len: usize = 0,
|
|
container_size: usize = CONTAINER_INCREASE_RATE,
|
|
container: []*const Payment = undefined,
|
|
allocator: Allocator,
|
|
pub fn init(allocator: Allocator) !IndexContainerUnsafe {
|
|
const container = try allocator.alloc(*const Payment, CONTAINER_INCREASE_RATE);
|
|
|
|
return IndexContainerUnsafe{ .container = container, .allocator = allocator };
|
|
}
|
|
|
|
pub fn add(self: *IndexContainerUnsafe, payment: *const Payment) !void {
|
|
if (self.len == self.container_size) {
|
|
const new_container_size = newContainerSize(self.len);
|
|
self.container = try self.allocator.realloc(self.container, new_container_size);
|
|
|
|
self.container_size = new_container_size;
|
|
}
|
|
|
|
self.container[self.len] = payment;
|
|
self.len += 1;
|
|
}
|
|
|
|
pub fn find(self: *IndexContainerUnsafe, target: []const u8) ?*const Payment {
|
|
for (0..self.len) |i| {
|
|
if (mem.eql(u8, target, &self.container[i].id))
|
|
return self.container[i];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
pub fn reset(self: *IndexContainerUnsafe) void {
|
|
self.len = 0;
|
|
}
|
|
|
|
pub fn deinit(self: *IndexContainerUnsafe) void {
|
|
self.len = 0;
|
|
self.container_size = 0;
|
|
self.allocator.free(self.container);
|
|
}
|
|
};
|
|
|
|
pub const PaymentsRepository = struct {
|
|
len: usize = 0,
|
|
max_indexes: usize = 1,
|
|
database_size: usize,
|
|
payments: []Payment,
|
|
indexes: []IndexContainerUnsafe,
|
|
mutex: Mutex,
|
|
allocator: Allocator,
|
|
subscribers_insert: [2]*const SubscriberFunc = undefined,
|
|
subscribers_insert_len: usize = 0,
|
|
|
|
pub fn init(allocator: Allocator, database_size: usize) !PaymentsRepository {
|
|
const payments = try allocator.alloc(Payment, database_size);
|
|
|
|
const max_indexes = calculateMaxIndexes(database_size);
|
|
var indexes = try allocator.alloc(IndexContainerUnsafe, max_indexes);
|
|
|
|
for (0..indexes.len) |i| {
|
|
indexes[i] = try IndexContainerUnsafe.init(allocator);
|
|
}
|
|
|
|
return PaymentsRepository{ .allocator = allocator, .mutex = Mutex{}, .database_size = database_size, .payments = payments, .indexes = indexes, .max_indexes = max_indexes };
|
|
}
|
|
|
|
pub fn subscribeInsert(self: *PaymentsRepository, func: *const SubscriberFunc) void {
|
|
self.subscribers_insert[self.subscribers_insert_len] = func;
|
|
self.subscribers_insert_len += 1;
|
|
}
|
|
|
|
pub fn insert(self: *PaymentsRepository, payment: Payment) !void {
|
|
self.mutex.lock();
|
|
defer self.mutex.unlock();
|
|
|
|
if (self.len == self.database_size)
|
|
return Allocator.Error.OutOfMemory;
|
|
|
|
self.payments[self.len] = payment;
|
|
const id_hash = self.hash(&payment.id);
|
|
try self.indexes[id_hash].add(&self.payments[self.len]);
|
|
|
|
for (self.subscribers_insert[0..self.subscribers_insert_len]) |func| {
|
|
func(&self.payments[self.len]);
|
|
}
|
|
|
|
self.len += 1;
|
|
}
|
|
|
|
pub fn findById(self: *PaymentsRepository, id: []const u8) ?*const Payment {
|
|
self.mutex.lock();
|
|
defer self.mutex.unlock();
|
|
|
|
if (self.len == 0)
|
|
return null;
|
|
|
|
const id_hash = self.hash(id);
|
|
const ic = self.indexes[id_hash].find(id);
|
|
|
|
return ic;
|
|
}
|
|
|
|
pub fn reset(self: *PaymentsRepository) void {
|
|
self.mutex.lock();
|
|
defer self.mutex.unlock();
|
|
|
|
self.len = 0;
|
|
|
|
for (0..self.indexes.len) |i| {
|
|
self.indexes[i].reset();
|
|
}
|
|
}
|
|
|
|
pub fn periodSummary(self: *PaymentsRepository, from: ?DateTime, to: ?DateTime) PaymentsSummary {
|
|
self.mutex.lock();
|
|
const len = self.len;
|
|
self.mutex.unlock();
|
|
var payments_summary = PaymentsSummary{ .total_payments_processed = 0, .total_value = 0 };
|
|
|
|
if (from != null and to != null) {
|
|
for (0..len) |i| {
|
|
const payment = self.payments[i];
|
|
|
|
if (payment.requested_at.gt(from.?) and payment.requested_at.lt(to.?)) {
|
|
payments_summary.total_payments_processed += 1;
|
|
payments_summary.total_value += payment.amount;
|
|
}
|
|
}
|
|
} else if (from != null) {
|
|
for (0..len) |i| {
|
|
const payment = self.payments[i];
|
|
|
|
if (payment.requested_at.gt(from.?)) {
|
|
payments_summary.total_payments_processed += 1;
|
|
payments_summary.total_value += payment.amount;
|
|
}
|
|
}
|
|
} else if (to != null) {
|
|
for (0..len) |i| {
|
|
const payment = self.payments[i];
|
|
|
|
if (payment.requested_at.lt(to.?)) {
|
|
payments_summary.total_payments_processed += 1;
|
|
payments_summary.total_value += payment.amount;
|
|
}
|
|
}
|
|
} else {
|
|
for (0..len) |i| {
|
|
payments_summary.total_value += self.payments[i].amount;
|
|
}
|
|
payments_summary.total_payments_processed = len;
|
|
}
|
|
|
|
return payments_summary;
|
|
}
|
|
|
|
pub fn integrationSummary(self: *PaymentsRepository, from: ?DateTime, to: ?DateTime) PaymentsIntegrationSummary {
|
|
self.mutex.lock();
|
|
const len = self.len;
|
|
self.mutex.unlock();
|
|
var summary = PaymentsIntegrationSummary{
|
|
.default_total_payments_processed = 0,
|
|
.default_total_value = 0,
|
|
.fallback_total_payments_processed = 0,
|
|
.fallback_total_value = 0,
|
|
};
|
|
|
|
if (from != null and to != null) {
|
|
for (0..len) |i| {
|
|
const payment = self.payments[i];
|
|
|
|
if (payment.requested_at.gt(from.?) and payment.requested_at.lt(to.?)) {
|
|
if (payment.integration_status != .processed)
|
|
continue;
|
|
|
|
if (payment.processed_by == 1) {
|
|
summary.default_total_payments_processed += 1;
|
|
summary.default_total_value += payment.amount;
|
|
} else {
|
|
summary.fallback_total_payments_processed += 1;
|
|
summary.fallback_total_value += payment.amount;
|
|
}
|
|
}
|
|
}
|
|
} else if (from != null) {
|
|
for (0..len) |i| {
|
|
const payment = self.payments[i];
|
|
|
|
if (payment.requested_at.gt(from.?)) {
|
|
if (payment.integration_status != .processed)
|
|
continue;
|
|
|
|
if (payment.processed_by == 1) {
|
|
summary.default_total_payments_processed += 1;
|
|
summary.default_total_value += payment.amount;
|
|
} else {
|
|
summary.fallback_total_payments_processed += 1;
|
|
summary.fallback_total_value += payment.amount;
|
|
}
|
|
}
|
|
}
|
|
} else if (to != null) {
|
|
for (0..len) |i| {
|
|
const payment = self.payments[i];
|
|
|
|
if (payment.requested_at.lt(to.?)) {
|
|
if (payment.integration_status != .processed)
|
|
continue;
|
|
|
|
if (payment.processed_by == 1) {
|
|
summary.default_total_payments_processed += 1;
|
|
summary.default_total_value += payment.amount;
|
|
} else {
|
|
summary.fallback_total_payments_processed += 1;
|
|
summary.fallback_total_value += payment.amount;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
for (0..len) |i| {
|
|
const payment = self.payments[i];
|
|
|
|
if (payment.integration_status != .processed)
|
|
continue;
|
|
|
|
if (payment.processed_by == 1) {
|
|
summary.default_total_payments_processed += 1;
|
|
summary.default_total_value += payment.amount;
|
|
} else {
|
|
summary.fallback_total_payments_processed += 1;
|
|
summary.fallback_total_value += payment.amount;
|
|
}
|
|
}
|
|
}
|
|
|
|
return summary;
|
|
}
|
|
|
|
fn hash(self: *PaymentsRepository, s: []const u8) usize {
|
|
std.debug.assert(self.max_indexes > 0);
|
|
return hash_map.hashString(s) % self.max_indexes;
|
|
}
|
|
|
|
pub fn deinit(self: *PaymentsRepository) void {
|
|
self.mutex.lock();
|
|
defer self.mutex.unlock();
|
|
|
|
for (self.indexes) |ic| {
|
|
@constCast(&ic).deinit();
|
|
}
|
|
|
|
self.allocator.free(self.indexes);
|
|
|
|
self.len = 0;
|
|
self.database_size = 0;
|
|
self.max_indexes = 0;
|
|
|
|
self.allocator.free(self.payments);
|
|
}
|
|
};
|
|
|
|
fn fakeGuid(end: u32) [36]u8 {
|
|
var guid: [36]u8 = .{0} ** 36;
|
|
|
|
mem.writeInt(u32, guid[0..4], end, .little);
|
|
|
|
return guid;
|
|
}
|
|
|
|
test "expect add payment to index container" {
|
|
var index_container = try IndexContainerUnsafe.init(testing.allocator);
|
|
defer index_container.deinit();
|
|
|
|
const payment = Payment{ .id = fakeGuid(1), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2011-10-05T14:48:00.000Z") };
|
|
|
|
try index_container.add(&payment);
|
|
try index_container.add(&payment);
|
|
try index_container.add(&payment);
|
|
try index_container.add(&payment);
|
|
|
|
try expect(index_container.len == 4);
|
|
}
|
|
|
|
test "expect find inserted payment" {
|
|
var index_container = try IndexContainerUnsafe.init(testing.allocator);
|
|
defer index_container.deinit();
|
|
|
|
try index_container.add(&Payment{ .id = fakeGuid(1), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2011-10-05T14:48:00.000Z") });
|
|
try index_container.add(&Payment{ .id = fakeGuid(2), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2011-10-05T14:48:00.000Z") });
|
|
try index_container.add(&Payment{ .id = fakeGuid(3), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2011-10-05T14:48:00.000Z") });
|
|
try index_container.add(&Payment{ .id = fakeGuid(4), .amount = 23.0, .requested_at = try DateTime.ParseFromIso("2011-10-05T14:48:00.000Z") });
|
|
try index_container.add(&Payment{ .id = fakeGuid(5), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2011-10-05T14:48:00.000Z") });
|
|
|
|
const payment_target = index_container.find(&fakeGuid(4));
|
|
|
|
try expect(payment_target.?.amount == 23.0);
|
|
}
|
|
|
|
test "expect return null payment not exists" {
|
|
var index_container = try IndexContainerUnsafe.init(testing.allocator);
|
|
defer index_container.deinit();
|
|
|
|
try index_container.add(&Payment{ .id = fakeGuid(1), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2011-10-05T14:48:00.000Z") });
|
|
|
|
const payment_target = index_container.find(&fakeGuid(4));
|
|
|
|
try expect(payment_target == null);
|
|
}
|
|
|
|
test "expect allocate memory when container is full" {
|
|
const buffer: []u8 = try testing.allocator.alloc(u8, @sizeOf(*void) * 1000);
|
|
defer testing.allocator.free(buffer);
|
|
|
|
var fba = heap.FixedBufferAllocator.init(buffer);
|
|
const allocator = fba.allocator();
|
|
|
|
var index_container = try IndexContainerUnsafe.init(allocator);
|
|
defer index_container.deinit();
|
|
|
|
for (0..923) |i| {
|
|
try (&index_container).add(&Payment{ .id = fakeGuid(@intCast(i)), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2011-10-05T14:48:00.000Z") });
|
|
}
|
|
|
|
try expect(index_container.container_size == ((923 / CONTAINER_INCREASE_RATE) + 1) * CONTAINER_INCREASE_RATE);
|
|
}
|
|
|
|
test "expect OutOfMemory error when allocator not has memory" {
|
|
const buffer: []u8 = try testing.allocator.alloc(u8, @sizeOf(*void) * 109);
|
|
defer testing.allocator.free(buffer);
|
|
|
|
var fba = heap.FixedBufferAllocator.init(buffer);
|
|
const allocator = fba.allocator();
|
|
|
|
var index_container = try IndexContainerUnsafe.init(allocator);
|
|
defer index_container.deinit();
|
|
|
|
for (0..101) |i| {
|
|
(&index_container).add(&Payment{ .id = fakeGuid(@intCast(i)), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2011-10-05T14:48:00.000Z") }) catch |err| {
|
|
try expect(err == Allocator.Error.OutOfMemory);
|
|
return;
|
|
};
|
|
}
|
|
|
|
try expect(false);
|
|
}
|
|
|
|
test "expect insert payment into repository" {
|
|
var repository = try PaymentsRepository.init(testing.allocator, 100);
|
|
defer repository.deinit();
|
|
|
|
const payment1 = Payment{ .id = fakeGuid(1), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2011-10-05T14:48:00.000Z") };
|
|
const payment2 = Payment{ .id = fakeGuid(2), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") };
|
|
|
|
try repository.insert(payment1);
|
|
try repository.insert(payment2);
|
|
|
|
try expect(repository.len == 2);
|
|
}
|
|
|
|
test "expect OutOfMemory error when database is full" {
|
|
var repository = try PaymentsRepository.init(testing.allocator, 100);
|
|
defer (&repository).deinit();
|
|
|
|
for (0..101) |i| {
|
|
const payment = Payment{ .id = fakeGuid(@intCast(i)), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2011-10-05T14:48:00.000Z") };
|
|
(&repository).insert(payment) catch |err| {
|
|
try expect(err == Allocator.Error.OutOfMemory);
|
|
return;
|
|
};
|
|
}
|
|
|
|
try expect(false);
|
|
}
|
|
test "expect return payment if exists" {
|
|
var repository: *PaymentsRepository = @constCast(&try PaymentsRepository.init(testing.allocator, 100));
|
|
|
|
defer repository.deinit();
|
|
|
|
try repository.insert(Payment{ .id = fakeGuid(1), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(2), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(3), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(23), .amount = 42.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(4), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(7), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(10), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
|
|
const payment_result = repository.findById(&fakeGuid(23));
|
|
|
|
try expect(payment_result.?.amount == 42);
|
|
}
|
|
|
|
test "expect return null if payment not exists" {
|
|
var repository: *PaymentsRepository = @constCast(&try PaymentsRepository.init(testing.allocator, 100));
|
|
|
|
defer repository.deinit();
|
|
|
|
try repository.insert(Payment{ .id = fakeGuid(1), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(2), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(3), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(23), .amount = 42.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(4), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(7), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(10), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
|
|
const payment_result = repository.findById(&fakeGuid(42));
|
|
|
|
try expect(payment_result == null);
|
|
}
|
|
|
|
fn testConcurrenceInsert(repository: *PaymentsRepository, interations: usize) !void {
|
|
for (0..interations) |i| {
|
|
try repository.insert(Payment{ .id = fakeGuid(@intCast(i)), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2011-10-05T14:48:00.000Z") });
|
|
}
|
|
}
|
|
|
|
fn testConcurrenceFind(repository: *PaymentsRepository, interations: usize) void {
|
|
for (0..interations) |i| {
|
|
_ = repository.findById(&fakeGuid(@intCast(i)));
|
|
}
|
|
}
|
|
|
|
test "expect avoid concurrency" {
|
|
const interations = 100;
|
|
|
|
const buffer: []u8 = try testing.allocator.alloc(u8, calculateNecessaryMemory(interations));
|
|
defer testing.allocator.free(buffer);
|
|
|
|
var fba = heap.FixedBufferAllocator.init(buffer);
|
|
const allocator = fba.allocator();
|
|
|
|
var repository: *PaymentsRepository = @constCast(&try PaymentsRepository.init(allocator, interations));
|
|
defer repository.deinit();
|
|
|
|
const threadInsert1 = try Thread.spawn(.{}, testConcurrenceInsert, .{ repository, interations / 2 });
|
|
const threadInsert2 = try Thread.spawn(.{}, testConcurrenceInsert, .{ repository, interations / 2 });
|
|
const threadFind = try Thread.spawn(.{}, testConcurrenceFind, .{ repository, interations });
|
|
|
|
threadInsert1.join();
|
|
threadInsert2.join();
|
|
threadFind.join();
|
|
|
|
try expect(repository.len == interations);
|
|
}
|
|
|
|
test "expect reset repository and indexes" {
|
|
var repository: *PaymentsRepository = @constCast(&try PaymentsRepository.init(testing.allocator, 100));
|
|
|
|
defer repository.deinit();
|
|
|
|
try repository.insert(Payment{ .id = fakeGuid(1), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(2), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(3), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(23), .amount = 42.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(4), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(7), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(10), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
|
|
repository.reset();
|
|
|
|
try expect(repository.len == 0);
|
|
for (repository.indexes) |ic| {
|
|
try expect(ic.len == 0);
|
|
}
|
|
}
|
|
|
|
test "expect summary return correct value" {
|
|
var repository = try PaymentsRepository.init(testing.allocator, 100);
|
|
defer repository.deinit();
|
|
|
|
try repository.insert(Payment{ .id = fakeGuid(1), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(2), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(3), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(23), .amount = 42.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(4), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(7), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(10), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.000Z") });
|
|
|
|
const summary = repository.periodSummary(null, null);
|
|
|
|
try expect(summary.total_payments_processed == 7);
|
|
try expect(summary.total_value == 102.0);
|
|
}
|
|
|
|
test "expect summary return correct value with filter from" {
|
|
var repository = try PaymentsRepository.init(testing.allocator, 100);
|
|
defer repository.deinit();
|
|
|
|
const from = try DateTime.ParseFromIso("2025-10-05T14:48:00.030Z");
|
|
|
|
try repository.insert(Payment{ .id = fakeGuid(1), .amount = 42.0, .requested_at = try DateTime.ParseFromIso("2024-10-05T13:47:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(2), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2024-10-05T14:48:00.033Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(3), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-09-05T14:48:00.100Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(4), .amount = 23.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.030Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(7), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.100Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(6), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:50:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(5), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-11-05T14:48:00.030Z") });
|
|
|
|
const summary = repository.periodSummary(from, null);
|
|
|
|
try expect(summary.total_payments_processed == 4);
|
|
try expect(summary.total_value == 53.0);
|
|
}
|
|
|
|
test "expect summary return correct correct value with filter to" {
|
|
var repository = try PaymentsRepository.init(testing.allocator, 100);
|
|
defer repository.deinit();
|
|
|
|
const to = try DateTime.ParseFromIso("2025-10-05T14:48:00.030Z");
|
|
|
|
try repository.insert(Payment{ .id = fakeGuid(1), .amount = 42.0, .requested_at = try DateTime.ParseFromIso("2024-10-05T13:47:00.000Z") }); // x
|
|
try repository.insert(Payment{ .id = fakeGuid(2), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2024-10-05T14:48:00.033Z") }); // x
|
|
try repository.insert(Payment{ .id = fakeGuid(3), .amount = 11.0, .requested_at = try DateTime.ParseFromIso("2025-09-05T14:48:00.100Z") }); // x
|
|
try repository.insert(Payment{ .id = fakeGuid(4), .amount = 23.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.030Z") }); // x
|
|
//
|
|
try repository.insert(Payment{ .id = fakeGuid(6), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:50:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(7), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.100Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(5), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-11-05T14:48:00.030Z") });
|
|
|
|
const summary = repository.periodSummary(null, to);
|
|
|
|
try expect(summary.total_payments_processed == 4);
|
|
try expect(summary.total_value == 86.0);
|
|
}
|
|
|
|
test "expect summary return correct value with filter from and to" {
|
|
var repository = try PaymentsRepository.init(testing.allocator, 100);
|
|
defer repository.deinit();
|
|
|
|
const from = try DateTime.ParseFromIso("2025-10-05T14:48:00.030Z");
|
|
const to = try DateTime.ParseFromIso("2025-10-05T14:48:00.100Z");
|
|
|
|
try repository.insert(Payment{ .id = fakeGuid(1), .amount = 42.0, .requested_at = try DateTime.ParseFromIso("2024-10-05T13:47:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(2), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2024-10-05T14:48:00.033Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(3), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-09-05T14:48:00.100Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(4), .amount = 23.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.030Z") }); // x
|
|
try repository.insert(Payment{ .id = fakeGuid(5), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-11-05T14:48:00.030Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(6), .amount = 10.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:50:00.000Z") });
|
|
try repository.insert(Payment{ .id = fakeGuid(7), .amount = 42.0, .requested_at = try DateTime.ParseFromIso("2025-10-05T14:48:00.100Z") }); // x
|
|
|
|
const summary = repository.periodSummary(from, to);
|
|
|
|
try expect(summary.total_payments_processed == 2);
|
|
try expect(summary.total_value == 65.0);
|
|
}
|