Skip to content

Commit

Permalink
The rule engine is working
Browse files Browse the repository at this point in the history
  • Loading branch information
Dracks committed Nov 11, 2024
1 parent 2563cac commit 76ad8e8
Show file tree
Hide file tree
Showing 9 changed files with 503 additions and 75 deletions.
261 changes: 235 additions & 26 deletions src/swift-server-tests/rules.tests.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Fluent
import QueuesFluentDriver
import XCTQueues
import XCTVapor
import XCTest
Expand All @@ -8,23 +9,24 @@ import XCTest
struct BaseCondition {
let operation: ConditionOperation
let valueStr: String?
let valueFloat: Double?
let valueDouble: Double?

init(operation: ConditionOperation, valueStr: String) {
self.operation = operation
self.valueStr = valueStr
self.valueFloat = nil
self.valueDouble = nil
}

init(operation: ConditionOperation, valueFloat: Double) {
init(operation: ConditionOperation, valueDouble: Double) {
self.operation = operation
self.valueStr = nil
self.valueFloat = valueFloat
self.valueDouble = valueDouble
}
}

final class RulesTests: AbstractBaseTestsClass {
private func createBasicRule(
var operationToLabel: [ConditionOperation: UUID] = [:]
private func createRule(
on db: Database, for userGroup: UserGroup,
with conditions: BaseCondition..., toApply labels: Label...
) async throws -> Rule {
Expand All @@ -39,7 +41,7 @@ final class RulesTests: AbstractBaseTestsClass {
ruleId: ruleId,
operation: cond.operation,
valueStr: cond.valueStr,
valueFloat: cond.valueFloat
valueDouble: cond.valueDouble
), on: db)
}

Expand All @@ -54,37 +56,59 @@ final class RulesTests: AbstractBaseTestsClass {
return rule
}

private func createBasicRuleAndRegister(
on db: Database, for userGroup: UserGroup, with condition: BaseCondition,
toApply label: Label
) async throws -> Rule {
let rule = try await self.createRule(
on: db, for: userGroup, with: condition, toApply: label)
operationToLabel[condition.operation] = label.id!
return rule
}

private func createBasicRules() async throws {
let db = app!.db

// String operations
let _ = try await createBasicRule(
let _ = try await createBasicRuleAndRegister(
on: db, for: testGroup, with: .init(operation: .prefix, valueStr: "needle"),
toApply: labels[0])
let _ = try await createBasicRule(
let _ = try await createBasicRuleAndRegister(
on: db, for: testGroup,
with: .init(operation: .regularExpression, valueStr: "needle"),
with: .init(operation: .regularExpression, valueStr: "A{2}B{2}"),
toApply: labels[1])
let _ = try await createBasicRule(
let _ = try await createBasicRuleAndRegister(
on: db, for: testGroup, with: .init(operation: .suffix, valueStr: "needle"),
toApply: labels[2])
let _ = try await createBasicRule(
let _ = try await createBasicRuleAndRegister(
on: db, for: testGroup,
with: .init(operation: .contains, valueStr: "needle"), toApply: labels[3])
// Float operations
let _ = try await createBasicRule(
on: db, for: testGroup, with: .init(operation: .greater, valueFloat: 1),
let _ = try await createBasicRuleAndRegister(
on: db, for: testGroup, with: .init(operation: .greater, valueDouble: 1),
toApply: labels[4])
let _ = try await createBasicRule(
let _ = try await createBasicRuleAndRegister(
on: db, for: testGroup,
with: .init(operation: .greaterEqual, valueFloat: 1), toApply: labels[5])
let _ = try await createBasicRule(
on: db, for: testGroup, with: .init(operation: .less, valueFloat: -1),
with: .init(operation: .greaterEqual, valueDouble: 1), toApply: labels[5])
let _ = try await createBasicRuleAndRegister(
on: db, for: testGroup, with: .init(operation: .less, valueDouble: -1),
toApply: labels[6])
let _ = try await createBasicRule(
on: db, for: testGroup, with: .init(operation: .lessEqual, valueFloat: -1),
let _ = try await createBasicRuleAndRegister(
on: db, for: testGroup, with: .init(operation: .lessEqual, valueDouble: -1),
toApply: labels[7])
}

private func checkContainsOperationLabel(
_ labels: [UUID], for operation: ConditionOperation, context: String
) throws {
let label = operationToLabel[operation]
guard let label else {
print("Label for operation \(operation) not found!")
throw TestError()
}
XCTAssertTrue(
labels.contains(label), "For operation \(operation) with context \(context)"
)
}

func testBaseLabelAssignation() async throws {
Expand All @@ -97,6 +121,7 @@ final class RulesTests: AbstractBaseTestsClass {
transactionFactory.build {
$0.$groupOwner.id = testGroupId
$0.movementName = "no-match"
$0.value = 0
return $0
},

Expand All @@ -112,13 +137,34 @@ final class RulesTests: AbstractBaseTestsClass {
$0.value = 1
return $0
},
transactionFactory.build {
$0.$groupOwner.id = testGroupId
$0.movementName = "in the middle is the needle only contains"
$0.value = 2
return $0
},

transactionFactory.build {
$0.$groupOwner.id = testGroupId
$0.movementName = "suffix with the needle"
$0.value = -2
return $0
},
transactionFactory.build {
$0.$groupOwner.id = testGroupId
$0.movementName = "AABBCCDD"
$0.value = -2
return $0
},
]

for transaction in transactions {
let _ = try await bankTransactionService.addTransaction(
on: app.db, withQueue: app.queues.queue, transaction: transaction)
}

try await app.queues.queue.worker.run()

for transaction in transactions {
try await transaction.$labels.load(on: app.db)
}
Expand All @@ -130,15 +176,178 @@ final class RulesTests: AbstractBaseTestsClass {
}

XCTAssertEqual(labelsForTransactions[0].count, 0)
XCTAssertEqual(labelsForTransactions[1].count, 3)
XCTAssertEqual(labelsForTransactions[2].count, 0)

/*XCTAssertEqual(
app.queues.asyncTest.jobs.count { $0.value.jobName == String(describing: NewTransactionJob.self)},
2,
"Expected 2 jobs to have been dispatched"
)*/
XCTAssertEqual(app.queues.asyncTest.jobs.count, 3)
XCTAssertEqual(labelsForTransactions[1].count, 3)
try checkContainsOperationLabel(
labelsForTransactions[1], for: .prefix,
context: transactions[1].movementName)
try checkContainsOperationLabel(
labelsForTransactions[1], for: .contains,
context: transactions[1].movementName)
try checkContainsOperationLabel(
labelsForTransactions[1], for: .greaterEqual,
context: transactions[1].movementName)

XCTAssertEqual(labelsForTransactions[3].count, 3)
try checkContainsOperationLabel(
labelsForTransactions[3], for: .greater,
context: transactions[3].movementName)
try checkContainsOperationLabel(
labelsForTransactions[3], for: .contains,
context: transactions[3].movementName)
try checkContainsOperationLabel(
labelsForTransactions[3], for: .greaterEqual,
context: transactions[3].movementName)

XCTAssertEqual(labelsForTransactions[4].count, 4)
try checkContainsOperationLabel(
labelsForTransactions[4], for: .contains,
context: transactions[4].movementName)
try checkContainsOperationLabel(
labelsForTransactions[4], for: .suffix,
context: transactions[4].movementName)
try checkContainsOperationLabel(
labelsForTransactions[4], for: .lessEqual,
context: transactions[4].movementName)
try checkContainsOperationLabel(
labelsForTransactions[4], for: .less, context: transactions[4].movementName)
}

func testConditionsRelation() async throws {
let app = try getApp()
let testGroupId = try testGroup.requireID()

let _ = try await createRule(
on: app.db, for: testGroup,
with: .init(operation: .prefix, valueStr: "needle"),
.init(operation: .less, valueDouble: 0),
toApply: labels[0])
let rule = try await createRule(
on: app.db, for: testGroup,
with: .init(operation: .prefix, valueStr: "needle"),
.init(operation: .less, valueDouble: 0),
toApply: labels[1])
rule.conditionsRelation = .notAnd
try await rule.save(on: app.db)

let transactions = [
transactionFactory.build {
$0.$groupOwner.id = testGroupId
$0.movementName = "needle"
$0.value = -11
return $0
},
transactionFactory.build {
$0.$groupOwner.id = testGroupId
$0.movementName = "there is no match on the needle"
$0.value = -11
return $0
},
transactionFactory.build {
$0.$groupOwner.id = testGroupId
$0.movementName = "needle"
$0.value = 2
return $0
},
transactionFactory.build {
$0.$groupOwner.id = testGroupId
$0.movementName = "there is no match on the needle"
$0.value = 2
return $0
},
]

for transaction in transactions {
let _ = try await bankTransactionService.addTransaction(
on: app.db, withQueue: app.queues.queue, transaction: transaction)
}

try await app.queues.queue.worker.run()

for transaction in transactions {
try await transaction.$labels.load(on: app.db)
}

let labelsForTransactions = transactions.map {
$0.labels.map { label in
return label.id!
}
}

XCTAssertEqual(labelsForTransactions[0].count, 1)
XCTAssertEqual(labelsForTransactions[0].first, labels[0].id)
XCTAssertEqual(labelsForTransactions[1].count, 1)
XCTAssertEqual(labelsForTransactions[1].first, labels[0].id)
XCTAssertEqual(labelsForTransactions[2].count, 1)
XCTAssertEqual(labelsForTransactions[2].first, labels[0].id)

XCTAssertEqual(labelsForTransactions[3].count, 1)
XCTAssertEqual(labelsForTransactions[3].first, labels[1].id)
}

func testRuleParentChildrenRelation() async throws {
let app = try getApp()
let testGroupId = try testGroup.requireID()

let rule1 = try await createRule(
on: app.db, for: testGroup,
with: .init(operation: .prefix, valueStr: "needle"),
toApply: labels[0])
let rule2 = try await createRule(
on: app.db, for: testGroup,
with: .init(operation: .less, valueDouble: 0),
toApply: labels[1])
rule2.$parent.id = try rule1.requireID()
try await rule2.save(on: app.db)

let transactions = [
transactionFactory.build {
$0.$groupOwner.id = testGroupId
$0.movementName = "needle"
$0.value = -11
return $0
},
transactionFactory.build {
$0.$groupOwner.id = testGroupId
$0.movementName = "there is no match on the needle"
$0.value = -11
return $0
},
transactionFactory.build {
$0.$groupOwner.id = testGroupId
$0.movementName = "needle"
$0.value = 2
return $0
},
transactionFactory.build {
$0.$groupOwner.id = testGroupId
$0.movementName = "there is no match on the needle"
$0.value = 2
return $0
},
]

for transaction in transactions {
let _ = try await bankTransactionService.addTransaction(
on: app.db, withQueue: app.queues.queue, transaction: transaction)
}

try await app.queues.queue.worker.run()

for transaction in transactions {
try await transaction.$labels.load(on: app.db)
}

let labelsForTransactions = transactions.map {
$0.labels.map { label in
return label.id!
}
}

XCTAssertEqual(labelsForTransactions[0].count, 2)
XCTAssertEqual(labelsForTransactions[1].count, 0)
XCTAssertEqual(labelsForTransactions[2].count, 1)
XCTAssertEqual(labelsForTransactions[3].count, 0)
}
}
2 changes: 1 addition & 1 deletion src/swift-server/configure.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ public func configure(_ app: Application) async throws {
app.queues.add(NewTransactionJob())
app.queues.configuration.workerCount = 1
try app.queues.startInProcessJobs(on: .default)
try app.queues.startScheduledJobs()
//try app.queues.startScheduledJobs()
} catch {
print(error)
throw error
Expand Down
Loading

0 comments on commit 76ad8e8

Please sign in to comment.